<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>umbrant blog</title><link href="http://www.umbrant.com/blog/atom.xml" rel="self"/><link href="http://www.umbrant.com"/><updated>2017-09-03T17:22:50Z</updated><id>http://www.umbrant.com</id><entry><title>Paper review: Relational Cloud, Database Scalability</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/relational_cloud_and_database_scalability.html"/><updated>2011-12-11T22:15:00Z</updated><published>2011-12-11T22:15:00Z</published><id>http://www.umbrant.com/blog/2011/relational_cloud_and_database_scalability.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a combined paper review for &quot;Relational Cloud: A Database-as-a-Service for the Cloud&quot;, a paper from MIT published at CIDR &#39;11, and &quot;Database Scalability, Elasticity, and Autonomy in the Cloud&quot;, an extended abstract from UCSB which appeared in DASFAA &#39;11. These papers deal with the strategies used to transition databases and storage systems to the unique challenges of the cloud&amp;nbsp;environment.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Database&amp;nbsp;Scalability&lt;/h3&gt;
&lt;p&gt;I&#39;m covering the UCSB paper first, since it&#39;s essentially a survey paper. It covers the required properties of a cloud storage system or database, and the different techniques used to achieve these properties. The important thing to remember here is that it&#39;s always a game of tradeoffs and choosing your point in the design space; databases (including cloud databases) normally mean ACID properties and an SQL interface, storage systems (BigTable, Dynamo) normally mean a wider array of consistency guarantees and a more programmatic interface. Once you&#39;ve chosen what exact consistency properties or programming interface you want, the underlying techniques used are about the&amp;nbsp;same.&lt;/p&gt;
&lt;p&gt;As is stated in the title, there are some core requirements for any database or storage system in the cloud: scalability, elasticity, and autonomy. Scalability means scale-out, the ability to use multiple nodes to gain increased storage capacity and performance. Elasticity is one of the core selling-points of the cloud: pay-as-you-go pricing as a cloud consumer, adding and removing nodes in response to the load on your service. Autonomy just refers to the ability to do these things automatically and reducing management overhead, since people are expensive, and the cloud means that you could potentially be dealing with lot of nodes (and thus potentially a log of&amp;nbsp;people).&lt;/p&gt;
&lt;p&gt;The paper then establishes the design space. Pure key-value stores are an unfriendly programming model (not enough consistency), but you also can&#39;t just run MySQL in the cloud, meaning that you want something in between. The authors describe taking a key-value store and providing strong consistency on an entity group (think Megastore) to be &lt;em&gt;data fusion&lt;/em&gt;, while taking a database and sharding it to be &lt;em&gt;data fission&lt;/em&gt; (of which Relational Cloud is an example). They both share the same property of intra-group/shard operations being efficient (on the same node), but cross-group/shard operations being expensive (two-phase commit!). Like I said before, they end up sounding about the same once you choose the same point in the design&amp;nbsp;space.&lt;/p&gt;
&lt;p&gt;The real difference here is in the provided API. A data fusion approach is more explicit about performance, since going cross-group to do an expensive operation requires more code wrangling (do 2PC yourself). On the other hand, data fission will still run your naughty cross-shard SQL query, it&#39;ll just do it slowly. Partitioning data in data fusion is also generally more explicit (Megastore makes you define your entity groups), while data fission tries to do it automatically under-the-hood based on your query access&amp;nbsp;pattern.&lt;/p&gt;
&lt;h3&gt;Relational&amp;nbsp;Cloud&lt;/h3&gt;
&lt;p&gt;This is essentially an implementation of a data fission approach from the MIT databases group. They identify many of the same points brought up in the UCSB paper, and add another of their own:&amp;nbsp;privacy.&lt;/p&gt;
&lt;p&gt;Scalability is achieved through data partitioning. Rel Cloud uses a graph partitioning strategy to identify min cuts on a graph representing query execution traces, basically trying to group together data that is used together. The clear problem here is speed. It&#39;s slow to turn a cloud database worth of tuples into a graph, run the partitioning algo, and then move the data around. Ideally, the system would be able to do this in reaction to load spikes (on the order of minutes), but that&#39;s unlikely. Unless the algo is weighted properly too, it could result in bad &quot;full shuffle&quot; data movement patterns, and the inability to manually tune is classic monolithic &quot;let us handle everything&quot; database&amp;nbsp;thinking.&lt;/p&gt;
&lt;p&gt;Rel Cloud introduces &lt;em&gt;Kairos&lt;/em&gt; to take care of autonomous elasticity. It monitors load and the current working set of the database, and adds or removes nodes in response to this. Kairos also can migrate data partitions to take care of load imbalances. It also does pretty deep modeling of I/O performance to figure out the capacity of the system, which I&#39;d like to hear more&amp;nbsp;about.&lt;/p&gt;
&lt;p&gt;The final properties is privacy. This refers to CryptDB, a paper that is also on the reading list for CS294. It essentially is a way of doing a limited subset of SQL operations on encrypted data, where the data is stored in the most secure format that can still support the requested operation. In this way, the database only ever sees encrypted data, though there are some assumptions about keys and the threat model that I find slightly&amp;nbsp;unconvincing.&lt;/p&gt;
&lt;h3&gt;Analysis&lt;/h3&gt;
&lt;p&gt;It&#39;s important to realize that there&#39;s always going to be a core group of business users who won&#39;t want to learn some new API, and for whom a data fission/Rel Cloud approach is the only solution they&#39;re ever going to use. SQL, even if the database community disagrees, is one of the defining attributes of a database, and that&#39;s a major selling point to some. Data fusion key-value stores are well and fine for hip Ruby-on-Rails hackers and Google, but small or medium sized businesses that don&#39;t own their own datacenter but want to use the cloud probably want the Rel Cloud database-as-a-service. They want something that looks just like a normal DBMS, but has the additional scalability, elasticity, and autonomy properties that come with the&amp;nbsp;cloud.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Bigtable</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/bigtable_review.html"/><updated>2011-12-11T21:32:00Z</updated><published>2011-12-11T21:32:00Z</published><id>http://www.umbrant.com/blog/2011/bigtable_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Bigtable: A Distributed Storage System for Structured Data&quot;, published at OSDI in 2006. This is Google&#39;s columnar key-value store built on top of GFS, and I believe that it&#39;s the preferred storage system within Google. It&#39;s also important to remember that even though it has rows, columns, and the word &quot;table&quot; in the name, it&#39;s doesn&#39;t provide attributed traditionally associated with a&amp;nbsp;database.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Highlights&lt;/h3&gt;
&lt;p&gt;BigTable uses a three-level indexing scheme to resolve a value: &lt;code&gt;(row, column, time)&lt;/code&gt;. This &lt;code&gt;time&lt;/code&gt; field is the surprising addition; apparently it&#39;s used for versioning and garbage collection of old values (an optional, per-table feature). It also allows multiple values for the same &lt;code&gt;(row,column)&lt;/code&gt; tuple. Tables in BigTable are also sparse and stored in columnar format, meaning that any given row probably only populates a fraction of the hundreds of columns in a table. The number of columns in a table isn&#39;t limited, but columns do each have to belong to a single &lt;em&gt;column family&lt;/em&gt;, which is a more permanent entity. Column families are used as a means of access control, and maybe also to optimize access&amp;nbsp;patterns.&lt;/p&gt;
&lt;p&gt;One thing that kind of threw me at first is how columns are used in BigTable. Unlike traditional RDBMS where the table schema is fairly fixed, BigTable encourages the developer to add lots more columns, in fact storing &lt;em&gt;data&lt;/em&gt; as a new column. This is demonstrated in their web page example; their schema actually adds a new column for each webpage, and each domain that links to the webpage. It&#39;s really best to just think of it as a big, columnar, scalable key-value&amp;nbsp;store.&lt;/p&gt;
&lt;p&gt;BigTable also builds heavily on other internal Google systems. It uses GFS for persistent storage of data. Chubby is used heavily for things like bootstrapping connections to BigTable, master election, detecting partitions, storing BigTable schema, and access control. Note that a single BigTable tablet server handles all the reads and writes for a tablet; GFS takes care of durability, so all BigTable has to watch out for is load imbalance (which can be handled by migrating tablets to other tablet servers and&amp;nbsp;caching).&lt;/p&gt;
&lt;p&gt;Metadata look ups all happen in memory, meaning reads take just a single disk access if they aren&#39;t served from cache. Writes happen to a commit log stored durably in GFS, and kept in memory in a &lt;em&gt;memtable&lt;/em&gt;. When the memtable fills up, the log gets compacted and then written to disk as an &lt;em&gt;SSTable&lt;/em&gt; (which is an immutable key-value format). SSTables are merged periodically, otherwise an awful lot of SSTables could have to be read on&amp;nbsp;recovery.&lt;/p&gt;
&lt;h3&gt;Analysis&lt;/h3&gt;
&lt;p&gt;Compactions are expensive and can lead to bursty performance. Tablet servers also aren&#39;t necessarily located with the corresponding GFS node that durably stores the tablet, meaning reads often have to go remote. Load imbalances also seem like they&#39;d be a big problem, even with the ability to split and migrate tablets, since only only one node can handle reads and writes (which does simplify&amp;nbsp;consistency).&lt;/p&gt;
&lt;p&gt;All that said, it does scale up pretty huge, and sees widespread use within Google. There are lots of good points too. I like their shootdown protocol for maintaining BigTable membership; if the master can&#39;t reach a tablet server, it force-removes its Chubby lock to make sure the tablet server kills itself. Masters also kill themselves if they lose their Chubby lock on master-ship. This prevents partitions from leading to divergent state, and is a cute idea (despite the name and terminology). BigTable also shows that a simple design with just row-level transactions can work for a broad array of applications, which is a heartening thought for the systems guy in&amp;nbsp;me. &lt;/p&gt;

   </content></entry><entry><title>Paper review: DryadLINQ and FlumeJava</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/dryadlinq_flumejava_review.html"/><updated>2011-11-02T16:57:00Z</updated><published>2011-11-02T16:57:00Z</published><id>http://www.umbrant.com/blog/2011/dryadlinq_flumejava_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I&#39;m combining two paper reviews this time, for &quot;FlumeJava: Easy, Efficient Data-Parallel Pipelines&quot; (PLDI &#39;10) and &quot;DryadLINQ: A System for General-Purpose Distributed Data-Parallel Computing Using a High-Level Language&quot; (OSDI &#39;08). These are both high-level languages that compile down to the MapReduce and Dryad respectively, and I think share a lot of&amp;nbsp;similarities.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main Ideas of&amp;nbsp;DryadLINQ&lt;/h3&gt;
&lt;p&gt;The basic premise for both FlumeJava and DryadLINQ is that it&#39;s hard to write &quot;raw&quot; MapReduce or Dryad programs (especially true for Dryad), and that really, they should be treated as an underlying execution engine for higher-level, declarative DSL embedded directly in a common productivity language. These higher-level languages then compile down into a lower-level execution plan (e.g., MapReduce jobs, or a Dryad DAG). This makes it somewhat analogous to how a database works, especially true since LINQ can use SQL Server as an execution engine. It doesn&#39;t enforce a schema or give the other nice properties of a traditional RDBMS, and it lets queries be written in either a more SQL-like or a more object-oriented&amp;nbsp;approach.&lt;/p&gt;
&lt;p&gt;Talking a little more about LINQ, it&#39;s a query language that can be used directly in .NET languages. Microsoft basically swapped out the normal SQL Server backend for Dryad, meaning that there&#39;s excellent language integration because of the maturity of LINQ. The &quot;schema&quot; is thus defined by the application&#39;s use of datatypes, rather than enforced by an underlying schema. This abstraction is made even better by the fact that DryadLINQ will automatically partition data across nodes (I think according to access pattern or data&amp;nbsp;type).&lt;/p&gt;
&lt;p&gt;Because it compiles down to Dryad (a pretty flexible execution engine), it allows for optimizations beyond what is possible with MapReduce, namely in smartly reusing in-memory data, avoiding disk writes after each stage, more efficient modes of moving data, and more flexible execution DAGs. Furthermore, they can also do runtime optimization for making efficient aggregation&amp;nbsp;trees.&lt;/p&gt;
&lt;h3&gt;Main Ideas of&amp;nbsp;FlumeJava&lt;/h3&gt;
&lt;p&gt;FlumeJava is a pure Java library that provides special Java collections and operations which get translated into MapReduce jobs. It also serves a similar purpose as something like Pig, where one of the primary advantages is transparently chaining together multiple MapReduce jobs into a processing pipeline. FlumeJava also does a bunch of optimizations on the resulting dataflow graph to combine and optimize the different stages, but still has to deal with ultimately reading from and writing to disk between stages (unlike DryadLINQ, which support in-memory transfer). It also takes care of messy things like creating and deleting the inter-stage files, as you&#39;d&amp;nbsp;expect.&lt;/p&gt;
&lt;p&gt;The result is something that comes really close to the performance of a hand-optimized MapReduce pipeline, meaning there really isn&#39;t much reason for people to write raw MapReduce at Google anymore. Since it&#39;s just a library, it&#39;s easy to bring the same sort of functionality to other languages. FlumeC++ already exists, and it shouldn&#39;t be that hard to make a FlumePython or the like&amp;nbsp;too.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;Relevance&lt;/h3&gt;
&lt;p&gt;Not writing raw MapReduce / Dryad code is a lesson we&#39;ve learned from all of the higher level languages (Pig, Hive, Spark, and these two). The future definitely looks more declarative, and I like the direct language integration of DryadLINQ and FlumeJava more than introducing a new DSL like Hive or Pig. It makes it effortless to do large scale computation in a language that you already&amp;nbsp;know.&lt;/p&gt;
&lt;p&gt;That said, all of these approaches are sort of converging. There really aren&#39;t that many types of operations that map well to the MapReduce model, and all the approaches pretty much have all of them. I don&#39;t think there&#39;s much more to be done here on the research front. It comes down to ease of use and debugging at this point rather than the programming model itself, which is actually one of the big wins of Pig (the debugging&amp;nbsp;console).&lt;/p&gt;

   </content></entry><entry><title>Paper review: PNUTS</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/pnuts_review.html"/><updated>2011-11-01T12:59:00Z</updated><published>2011-11-01T12:59:00Z</published><id>http://www.umbrant.com/blog/2011/pnuts_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;PNUTS: Yahoo!&#39;s Hosted Data Serving Platform&quot;, published at VLDB in 2008. This is a distributed, cross-datacenter key-value store that introduces the notion of &quot;timeline consistency&quot; for records, which is stronger than mere eventual consistency, and is still easy for programmers to reason about. One of my more favorite papers from the reading&amp;nbsp;list.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;I like PNUTS a lot, and it&#39;s always a little hard to criticize industry papers that present a real-world production system that is being used by thousands of internal programmers and serves millions of records a day. That said, PNUTS does a better job than others (&lt;em&gt;cough&lt;/em&gt; Dremel &lt;em&gt;cough&lt;/em&gt;) in presenting where it lies in the great distributed storage system design space. It&#39;s got the sweet spot of an architecture that is conceptually simple, a consistency model that is easy to understand, and a complete API. One of the core questions while I was reading Megastore was how application programmers were possibly supposed to possibly design their application and use this complicated system; PNUTS feels comparatively much easier. An easy to understand consistency model and API is actually way more important than slightly stronger guarantees, unless you&#39;re only designing for the ubermensch programmer (foolish, even at a place like&amp;nbsp;Google).&lt;/p&gt;
&lt;p&gt;So, lets talk about timeline consistency, the model presented (if not invented) by PNUTS. PNUTS uses a &lt;em&gt;record-level mastering&lt;/em&gt; scheme that requires each record to be &quot;owned&quot; by a single replica. All writes to this record have to go through this replica, meaning that we have record-level serializability (the same sort of guarantee given by lots of key-value stores). Write propagation is done asynchronously by using the pub/sub Yahoo! Message Broker to avoid synchronous inter-datacenter roundtrips. This means there is some potential durability/availability loss of writes if the YMB fails, but Raghu in his talk indicated this was a very low probability. There&#39;s also an write availability loss if the master replica goes down, since there might be pending writes at the&amp;nbsp;master.&lt;/p&gt;
&lt;p&gt;API wise, we&#39;re presented with a &quot;choose your own consistency&quot; model for reads and a test-and-set write operation, besides normal blind reads and writes. Blind reads and writes don&#39;t have any special semantics; timeline consistency says that reads are always consistent, just potentially stale. Reads can also specify a minimum version, or ask for a fully up-to-date version. Test-and-set write lets apps do lightweight optimistic concurrency, by doing a read (getting a value), and then doing a test-and-set write to only write if the version matches the version read, abort and retrying if&amp;nbsp;not.&lt;/p&gt;
&lt;p&gt;You can effectively emulate &quot;cross record&quot; transactions by packing all your data into the same PNUTS record or denormalizing (with, of course, a loss in flexibility or consistency), which might be why Raghu says that Yahoo!&#39;s developers don&#39;t need cross-record consistency&amp;nbsp;guarantees.&lt;/p&gt;
&lt;p&gt;PNUTS also will dynamically transfer master responsibilities to a geographic replica closer to where writes are being sent, to reduce latency. My impression is that this is a fairly lightweight operation, since all that really needs to happen is transferring the master bit, and delaying writes while waiting for the old master&#39;s writes to flush. YMB only gives total ordering on messages sent from the same datacenter, which is why the new master has to&amp;nbsp;wait.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;relevance&lt;/h3&gt;
&lt;p&gt;It&#39;s still an open question whether web applications really need multi-record transactions or not, since the claim by Yahoo! and the PNUTS team is that they haven&#39;t seen a need from their own developers. Staleness is okay, inconsistency and reordering is not. I find the consistency model easy to grok, and Raghu indicated that there&#39;s no real desire to significantly change or redesign the system. The paper states &quot;multi-record updates&quot; and &quot;eventual consistency&quot; as future work, but that hasn&#39;t happened in the 3 years since the paper was published because of a lack of demand. I find that tremendously interesting, and a very compelling backing for this intermediate kind of consistency&amp;nbsp;model.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Dremel</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/dremel_review.html"/><updated>2011-10-31T13:16:00Z</updated><published>2011-10-31T13:16:00Z</published><id>http://www.umbrant.com/blog/2011/dremel_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Dremel: Interactive Analysis of Web-Scale Datasets&quot;, published in short form at VLDB in 2010. This is a large-scale, interactive analytics engine built by Google that handles adhoc queries on terabytes to petabytes of data, returning aggregate results in seconds to minutes (and if that&#39;s not cool, I don&#39;t know what is). However, the paper didn&#39;t cover query execution, and instead talked about the novel, but kind of boring, nested hierarchical storage format. I&#39;m still hoping for a more systems-y paper in the&amp;nbsp;future.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;Dremel was optimized for one thing: scanning through lots of read-only data really fast, and generating aggregate results that reflect something like 99% or all of the data (99% letting you chop off the latency tail). This makes it great for doing adhoc drilldown analytics, when you&#39;re trying to poke at data from many angles to identify what you want, before writing a more involved analytics program in a different language to analyze it. It&#39;s really &lt;em&gt;not&lt;/em&gt; optimized for doing point lookups, updates, or more complicated analytics: big scans that result in aggregate numbers is the name of the game. Unlike Pig or Hive, it does this with its own custom query execution engine, rather than compiling down to the &quot;common substrate&quot; of&amp;nbsp;MapReduce.&lt;/p&gt;
&lt;p&gt;The nested data format makes use of &quot;repetition&quot; and &quot;definition&quot; fields to specify at what level in the hierarchy that any given value in the column is. These let us reconstruct the entire nested data structure by storing only the leaves of the tree, but means it has to do scans to find out which record any row belongs to since the position depends on all previous entries in the column. The use of null values and repetition and definition also allows really easy compression of null values and the &quot;wide but sparse&quot; style of BigTable where there are a lot of columns, but not all are are filled&amp;nbsp;out.&lt;/p&gt;
&lt;p&gt;The rest is less interesting. Dremel is queried in an SQL-like language, and works best when do aggregation results on a few columns. Doing many columns is expensive because they need to be joined. It also makes good use of multi-level aggregation trees to get better parallelism, since each individual aggregator has to process less data. In-memory caching and prefetching further improve performance. Because of the efficient data storage format, it often can read only an order of magnitude less data than a comparable row-oriented MR job. Going to Dremel&#39;s more efficient execution engine results in another order of magnitude&amp;nbsp;speedup.&lt;/p&gt;
&lt;p&gt;Fault-tolerance and straggler detection also play in to execution time. When trying to run a 10-sec query on thousands of nodes, it&#39;s very likely that you&#39;re going to be hitting a slow node or two. This is why Dremel allows for &quot;99.9%&quot; type results, that reflect almost all, but not quite all, of the&amp;nbsp;data.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;relevance&lt;/h3&gt;
&lt;p&gt;I like the idea of custom systems besides MapReduce built for specific tasks like this. Google chose to make a system that does one thing really well, with clear tradeoffs in terms of performance and features. They gave up the ability to modify the data or do point lookups, and resulted in a system that is two orders of magnitude better than MapReduce. There&#39;s clearly a need for more interactive query systems than MapReduce, though also clearly not as general purpose and not a complete MR&amp;nbsp;replacement.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Dynamo</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/dynamo_review.html"/><updated>2011-10-27T10:35:00Z</updated><published>2011-10-27T10:35:00Z</published><id>http://www.umbrant.com/blog/2011/dynamo_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review for Dynamo, the tunable consistency/availability/durability key-value store built by Amazon. It&#39;s based on the Chord DHT, and was published at SOSP in 2007. It&#39;s also one of my favorite&amp;nbsp;papers.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;idea&lt;/h3&gt;
&lt;p&gt;This is an industrial paper, so the novelty comes from the engineering effort that goes into making Chord practical for the datacenter. The authors clearly did their homework before building the system, resulting in the practical application of a number of different&amp;nbsp;techniques.&lt;/p&gt;
&lt;p&gt;I want to start by talking about the usecase that Dynamo was designed for. A DHT key-value store has the major benefits (generally speaking) of being relatively simple, quite fault-tolerant, good at spreading load, and easy to scale. The downsides (again, generally speaking) are the slightly erratic behavior in terms of consistency and routing performance, and undefined behavior when it comes to actually storing and moving data around. Chord, for instance, defines just a routing protocol. After you finish hopping around to get to the node with a certain key, the data itself isn&#39;t necessarily on that node (instead, the true location of the data, meaning one more&amp;nbsp;lookup).&lt;/p&gt;
&lt;p&gt;Amazon&#39;s primary usecase for Dynamo seems to be for its shopping cart, where it&#39;s really important to have highly available, even if slightly inconsistent, writes. This works really well since shopping cart updates are pretty commutative; it&#39;s easy to just take the union of divergent shopping carts, and reach a mostly consistent state. There can still be problems (what if the user adds the same item once in each cart? What if they add and delete in one and add in the other?), but these can be kicked up to the user at checkout time and resolved manually. It&#39;s not to say that this happens very often at all, but when nodes do fail, almost normal-looking operation can&amp;nbsp;continue.&lt;/p&gt;
&lt;p&gt;The secret sauce here is Amazon&#39;s tunable R+W&amp;gt;N consistency model. The application programmer using Dynamo specifies the number of replicas that must be updated on a read (R) or write (W). As long as R+W is greater than N, the total number of replicas, we should be able to provide consistency to the user (assuming we can correctly merge writes). This means for a typical replication factor of N=3, the programmer can specify highly available writes and slower but consistent reads (3+1&amp;gt;3), a more balanced approach (2+2&amp;gt;3), or assuming a read-heavy workload (1+3&amp;gt;3). Increasing N increase the replication factor, meaning better durability. Choosing R+W&amp;leq;N lets you play the brave game of eventual consistency, relying on your merge function more to do the right&amp;nbsp;thing.&lt;/p&gt;
&lt;p&gt;A couple notes to close out. Amazon&#39;s metric for Dynamo was 99.9% percentile latency, the first time it was indicated to me that variation in latency, rather than average latency, is the real killer. Dynamo also utilized the Chord ring-membership protocol, but used O(1) routing instead of Chord routing since it&#39;s a datacenter environment where all the nodes are known and presumably long-lived. They used cool things like vector clocks and Merkle trees to do efficient detection and merging of updates. When the vector clocks diverge, the programmer has to provide the merge function (the default, and most heavily used, being last-writer wins). These, and other details, are what made it such a revelation to&amp;nbsp;me.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;relevance&lt;/h3&gt;
&lt;p&gt;I think all of academia had a love affair with DHTs for a while, because of all the nice probabilistic and mathematical properties that they have. Chord is still one of the coolest papers ever to me. However, for the datacenter environment, we have to wonder if this is the right model. I wonder how many of the properties of a DHT are really necessary. Fault-tolerance via replication is not unique to DHTs, neither is elastic scaling or load balancing. I find the &quot;choose your own consistency&quot; to be cool, but the apparent result was that most programmers just left everything at default. Default R, W, N, default merge function. Eventual consistency is also a weak model, and Dynamo can give you either fully consistent (slow, low availability), consistent if you rely on your merge function (dubious), or eventually consistent&amp;nbsp;(eww).&lt;/p&gt;
&lt;p&gt;Thus, I&#39;m making the call that for datacenters, pure DHTs like Dynamo don&#39;t really make sense. We need a stronger consistency model, and we need it to be more automatic and easy for programmers to reason&amp;nbsp;about.&lt;/p&gt;

   </content></entry><entry><title>Paper review: SCADS</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/scads_paper_review.html"/><updated>2011-10-12T15:51:00Z</updated><published>2011-10-12T15:51:00Z</published><id>http://www.umbrant.com/blog/2011/scads_paper_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;SCADS: Scale-Independent Storage for Social Computing Applications&quot; by Armbrust et al. This was published at CIDR in 2009. In a nutshell, SCADS is a key-value store that lets programmers choose their own consistency model and semantics, and restricts queries to be &quot;scale-independent&quot;, i.e. requiring a constant amount of&amp;nbsp;work.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;idea&lt;/h3&gt;
&lt;p&gt;I think SCADS chooses an interesting point in the scalable storage design space to focus&amp;nbsp;on.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple key-value storage&amp;nbsp;interface&lt;/li&gt;
&lt;li&gt;A query language that only allows constant-time requests (no O(n) operations that fail at&amp;nbsp;scale)&lt;/li&gt;
&lt;li&gt;Declarative, tunable consistency models, letting the programmer specify consistency at the level of application&amp;nbsp;requirements&lt;/li&gt;
&lt;li&gt;Scale-up/scale-down architecture designed for incremental cloud&amp;nbsp;pricing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes me feel the comparison against Facebook&#39;s heavily sharded MySQL cluster behind memcached is kind of unfair because they are pretty different usecases, but there is still a lot of merit behind the ideas in&amp;nbsp;SCADS.&lt;/p&gt;
&lt;p&gt;SCADS doesn&#39;t seem designed for ad-hoc queries, since handling requests in constant time can require building indexes, which is potentially quite expensive. Updating indexes is also a potentially high cost. I&#39;m not really sure how to keep both reads and writes in constant time here, since denormalizing means writes might require O(n)&amp;nbsp;writes.&lt;/p&gt;
&lt;p&gt;I really, really like the idea of declarative specifications for consistency, performance, and other application constraints. I feel like application developers really shouldn&#39;t have to reason about the details of replication, data placement, and consistency; they should be able to state what they want at a high level in terms of application requirements, and have the system figure out how to achieve this. This pushes the responsibility down to the people running the storage system, who are hopefully better able to reason about machine failure rates, the types of failures, and the consistency and durability properties of the&amp;nbsp;system.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;relevance&lt;/h3&gt;
&lt;p&gt;As I hinted above, I really like the idea of declarative specification of application requirements. It&#39;s not an easy problem to translate this into the low-level SLOs that can actually be enforced by a cluster resource manager, but it&#39;s a good one. Providing these guarantees to all the different kinds of applications running on a cluster is the end goal. This is probably hard to do generally without some application-level semantics about the incoming requests (SCADS and it&#39;s constant time only requests for&amp;nbsp;instance).&lt;/p&gt;
&lt;p&gt;At the very least, I&#39;d like to see this done for a more general purpose storage layer, perhaps something like GFS or&amp;nbsp;Bigtable.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Hive and Pig</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/hive_pig_paper_review.html"/><updated>2011-10-09T17:44:00Z</updated><published>2011-10-09T17:44:00Z</published><id>http://www.umbrant.com/blog/2011/hive_pig_paper_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Hive: Data Warehousing &lt;span class=&quot;amp&quot;&gt;&amp;amp;&lt;/span&gt; Analytics on Hadoop&quot; by the Facebook Data Team (a &lt;a href=&quot;http://www.slideshare.net/zshao/hive-data-warehousing-analytics-on-hadoop-presentation&quot;&gt;set of slides&lt;/a&gt;), and &quot;Pig Latin&quot; A Not-So-Foreign Language for Data Processing&quot; by Olston et al. The Hive slide deck I believe is from 2009, and Pig was published at SIGMOD in 2008. I supplemented this with the Hive paper published at VLDB in&amp;nbsp;2009.&lt;/p&gt;
&lt;p&gt;These are Facebook and Yahoo&#39;s approaches to higher-level languages that compile down to MapReduce on Hadoop. Measured by the percentage of Hive and Pig jobs on their production clusters, they have both been extremely successful. Hive takes a traditional SQL/database-like approach, while Pig looks more imperative. At face value they seem quite different, but there are actually a bunch of underlying&amp;nbsp;similarities.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Hive main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;Hive is effectively a traditional database that just uses HDFS and MapReduce for data storage and query execution. Tables are serialized and deserialized to files in HDFS, and can be partitioned across and within fields. The query language, HiveQL, looks exactly like SQL minus some of the more complicated operators because of engineering effort and the limitations of MapReduce. UDFs are also supported, meaning that normal MapReduce code can be slid right in. HiveQL is compiled down into a MR query plan which can consist of multiple MR jobs. The logical plan is optimized by a rule-based optimizer (future work being an adaptive cost-based&amp;nbsp;optimizer).&lt;/p&gt;
&lt;p&gt;Queries can be fed to the server via a Thrift server, which enables Hive usage from a variety of different programming languages. A small note is that table metadata is stored outside of HDFS, in a normal database. This is simply because the amount of data is small, and the access pattern is pretty random, making HDFS&amp;nbsp;ill-suited.&lt;/p&gt;
&lt;h3&gt;Pig main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;Pig is designed explicitly for ad-hoc data analysis by programmers. The query language looks like Python with operators pulled from SQL, and instead of tables, users are given more programmer-friendly data structures like maps and lists. UDFs are also first-class citizens in Pig, and can have arbitrary inputs and outputs (non-atomic&amp;nbsp;values).&lt;/p&gt;
&lt;p&gt;All this means that the query language and data format are more flexible. Hive needs to the classic ETL (extract-transform-load) to get data into tables before it can query it. Pig, you just pass it a file and a function explaining how to interpret it. This likely comes at a performance cost, but using the standard deserializers and a schema would ameliorate this. Pig also allows for more explicit control over the query plan, since each stage in the execution DAG is as&amp;nbsp;programmed.&lt;/p&gt;
&lt;p&gt;As in Hive, Pig does not provide some operators because of the limitations of MapReduce. Also as in Hive, statements using the SQL-like operators can be optimized, and multiple MapReduce jobs are chained together for you&amp;nbsp;automatically.&lt;/p&gt;
&lt;p&gt;One thing I really like about Pig is the focus on debugging. Trying to reason about a page of SQL is really difficult, and it&#39;s much easier to reason about Pig&#39;s series of steps. It looks extremely similar to how I do ad-hoc text parsing in Python: gradually applying operators to collections of strings until I get the result I want. Pig also provides an &quot;example execution table&quot; that shows what the Pig program does on a small amount of data, which is much quicker than running the actual MapReduce&amp;nbsp;jobs.&lt;/p&gt;
&lt;h3&gt;Analysis&lt;/h3&gt;
&lt;p&gt;It&#39;s handy that both Hive and Pig automatically string together MR jobs as part of one program, but you still pay the serialization overhead of writing things into intermediate files between jobs. This is something that isn&#39;t true with a more general execution framework like Dryad. The move towards more declarative languages, as I&#39;ve said previously, isn&#39;t surprising at all, since actually programming a MapReduce job is way more work than using something more high-level and declarative. For ad-hoc queries, it&#39;s way better to optimize for programmer productivity than try to squeeze out that last 20% of performance from writing it in raw&amp;nbsp;MR.&lt;/p&gt;
&lt;p&gt;Hive has been extremely popular at Facebook, and I think the same is true of Pig at Yahoo. I think the future is going to be improving the underlying Hadoop execution engine to better support ad-hoc queries by keeping intermediate files in memory, and improving the number of operators and optimizers for both&amp;nbsp;languages.&lt;/p&gt;

   </content></entry><entry><title>Paper review: MapReduce and Dryad</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/dryad_mapreduce_review.html"/><updated>2011-09-30T11:58:00Z</updated><published>2011-09-30T11:58:00Z</published><id>http://www.umbrant.com/blog/2011/dryad_mapreduce_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is another combined paper review, because the ideas are again pretty similar, and it&#39;s a useful compare/contrast. The first is the famous MapReduce paper from Google, and the second is Microsoft&#39;s response,&amp;nbsp;Dryad.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;MapReduce: Simplified Data Processing on Large Clusters&quot;, Dean and Ghemawat. Published at OSDI&amp;nbsp;2004.&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Dryad: Distributed Data-Parallel Programs from Sequential Building Blocks&quot;, Isard et al. Published at Eurosys&amp;nbsp;2007.&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main ideas from&amp;nbsp;MapReduce&lt;/h3&gt;
&lt;p&gt;MapReduce is a parallel data processing framework designed initially for a very specific task: scanning large amounts of textual data to create a web search index. It was essentially codesigned with GFS for this purpose. As a result, it boils down computation into just two phases: &lt;em&gt;map&lt;/em&gt;, followed by &lt;em&gt;reduce&lt;/em&gt;. Programmers have to write just two functions, one for each phase. Then, these two functions are run in massively parallel fashion: mappers run the map function, and the output from mappers is then fed to reducers, which run the reduce function. Mappers are scheduled for data-locality, moving computation to where data is stored to minimize network communication. The map phase essentially does some data-parallel operation, while the reduce phase aggregates results from the map phase to produce the final&amp;nbsp;output.&lt;/p&gt;
&lt;p&gt;The cool parts of this paper are twofold: first, that such a simple, limited programming model can accommodate such a wide variety of tasks, and that almost all of the complexity of running code on thousands of machines can be abstracted away from the&amp;nbsp;programmer.&lt;/p&gt;
&lt;p&gt;Regarding the programming model, it&#39;s something that can be taught in a matter of days. Map and reduce are familiar from functional programming languages, and really small amounts of code can do very powerful things. It is quite limited (only works for data-parallel operations), but when dealing with big data, your operations basically have to be data-parallel to complete in any reasonable time. Google used MapReduce to do a wide variety of tasks, so the proof of utility is in the&amp;nbsp;pudding.&lt;/p&gt;
&lt;p&gt;The distributed, fault tolerant framework is what really drew me in. A single master manages all the workers (which potentially means a single point of failure), but this is way less likely than a worker failure. Failure of workers is handled transparently, by restarting the worker. This is possible because the output from the map stage is durably written to disk storage, then read by the reducers. Mapper input, of course, is durably stored as well, so they can also be easily restarted. Another feature I liked is chopping off the latency tail caused by &quot;straggler&quot; workers by starting duplicate tasks towards the end of the&amp;nbsp;job.&lt;/p&gt;
&lt;p&gt;In summation, MapReduce is both a powerful and simple framework that saw a lot of use at Google for a variety of&amp;nbsp;tasks.&lt;/p&gt;
&lt;h3&gt;Main ideas from&amp;nbsp;Dryad&lt;/h3&gt;
&lt;p&gt;Dryad is what some people see as &quot;MapReduce done right&quot;, but this is a contentious claim. It&#39;s a more general framework in two important ways. First, it allows for more general styles of computation, meaning more than just two phases, and more than just map and reduce elements in the graph. Second, it allows communication between stages to happen over more than just files stored in the DFS: Dryad allows for sockets, shared memory, and pipes to be used as channels between elements. It ultimately ends up looking like a DAG of user-defined elements. Data flows between elements over a choice of channels, and the elements are all user-defined. This has a number of benefits: more efficient communication, the ability to chain together multiple stages, and express more complicated&amp;nbsp;computation.&lt;/p&gt;
&lt;p&gt;This leads to a number of complications. While it does subsume the MapReduce paradigm, with generality comes complexity. The programming model is nowhere near as simple (the authors cite &quot;a couple weeks&quot; to get started), and to me, it feels like doing the work of a database: designing all the elements and communication in a physical query plan, and optimizing it. They even do a direct comparison against SQL Server in the paper, in fact showing that they have similar query plans but Dryad comes out a little bit faster. Doing this really isn&#39;t simple at all, and the example queries they show do nothing to deny it. I consider dataflow programming (what this is, essentially) to be difficult to reason about for most&amp;nbsp;programmers.&lt;/p&gt;
&lt;p&gt;Dryad also incorporates the same fault-tolerance as MapReduce, and is able to restart failed tasks correctly. It also has this idea of &quot;dynamic runtime optimization&quot; which sounds very DB, and is hard for MapReduce to do since it&#39;s the equivalent of UDFs in a&amp;nbsp;database.&lt;/p&gt;
&lt;h3&gt;Comparison and&amp;nbsp;evaluation&lt;/h3&gt;
&lt;p&gt;My impression is that people were pretty unhappy with Dryad when it came out. It&#39;s not nearly as elegant as MapReduce, there aren&#39;t any cool operational insights, and feels very &quot;me too&quot;. However, as stated in the Dryad paper, programmers aren&#39;t really meant to interface with Dryad directly, and are instead supposed to use things like DryadLINQ (which turns declarative LINQ queries into Dryad execution graphs, exactly how everyone wanted). This is true for MapReduce too, since FlumeJava has seen heavy use at Google, and Hive and Pig dominate Hadoop workloads at Facebook. As nice and &quot;simple&quot; MapReduce is compared to Dryad, no one is directly programming on either these days, instead doing the DB-like thing and using declarative query&amp;nbsp;languages.&lt;/p&gt;
&lt;p&gt;Dryad also did correctly identify all the flaws with MapReduce, flaws that have to be papered over and hacked around to get the same kind of performance and generality. Hadoop is going to have to become more memory aware to eke out additional performance, and there are &quot;workflow management&quot; tools that allow chaining of multiple MapReduce jobs to effectively achieve multi-stage workflows. As long as the user never has to worry about the details, declarative execution engines built on top of Dryad rather than Hadoop have an&amp;nbsp;advantage.&lt;/p&gt;
&lt;p&gt;In terms of future relevance, I think that the basic idea of hiding faults and communication from the programmer is totally the right idea. It&#39;s way easier to write programs within a Dryad or MapReduce framework than something like MPI, which didn&#39;t hide anything. The DB community had it right though in calling for declarative query languages, and Hadoop and MapReduce these days are essentially being used as distributed query execution engines. I think we&#39;re going to see a wider variety of query languages in the future though, since there&#39;s a tradeoff between generality and simplicity. I doubt Hive and FlumeJava are the final word. There&#39;s also room for other types of query execution engines; Pregel&#39;s BSP is an&amp;nbsp;example.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Megastore</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/megastore_review.html"/><updated>2011-09-24T19:35:00Z</updated><published>2011-09-24T19:35:00Z</published><id>http://www.umbrant.com/blog/2011/megastore_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Megastore: Providing Scalable, Highly Available Storage for Interactive Services&quot; by Baker et al. This was published at CIDR in 2011. The basic idea is providing ACID semantics across geographically-distant datacenters with highly partitioned datasets and an efficient Paxos replication&amp;nbsp;scheme.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;The basic premise of Megastore is that some applications require strong ACID semantics, while also desiring the fault-tolerance that comes with cross-datacenter replication. They claim that existing solutions (like a heavily sharded MySQL database) do not fill this niche because they are hard to manage and scale, driving a need for Megastore. Megastore does this by asking application developers to partition their data into &lt;em&gt;entity groups&lt;/em&gt;, where each group represents a relatively small amount of data: the profile for one user, or a single blog account. Operations within the group get full ACID semantics; cross group operations have to build their own consistency model, perhaps two-phase commit, or something looser. Megastore also allows applications to do less-consistent reads for lower&amp;nbsp;latency.&lt;/p&gt;
&lt;p&gt;The data model and query language for Megastore also differs from traditional RDBMSs. The data model isn&#39;t relational since it&#39;s built on top of Bigtable (which in turn, is on top of GFS), but is still strongly-typed and consists of properties within tables. The query language is more limited; being based on Bigtable means that there isn&#39;t support for joins. This is fixed either by denormalizing the data, or doing it in application code. There seem to be a lot of tricks for creating indexes and doing data placement&amp;nbsp;efficiently.&lt;/p&gt;
&lt;p&gt;Log replication is done by using Paxos to resolve each log entry before applying it. Multiple writers race to get a single leader to accept their write; failed attempts have to be retried. Performance wise, they still have to do an inter-datacenter roundtrip even in the best case of a stable leader and being able to piggyback accepts and prepares. This means that they&#39;re never going to do better than a few writes per second; they quote a figure of 100-400ms latency per write. This is okay as long as the entity groups are small and the application write rates are thus low. Reads can be done without a roundtrip by having a special coordinator in each datacenter which tracks when replicas become out of&amp;nbsp;date.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;relevance&lt;/h3&gt;
&lt;p&gt;The biggest thing that stuck with me when reading this paper was that as a developer, this sounds really painful to use. Partitioning data that finely is painful, and you have to build inter-group consistency yourself. This indicates to me that schema changes might be common, but that&#39;s really painful since data is denormalized and there&#39;s all this schema-specific app-level code built on top to do joins and consistency. The claim that there is &quot;predictable performance&quot; from a lack of joins seems unsubstantiated. Building on top of Bigtable which is on top of Megastore means that it&#39;s very hard for developers to reason about what is actually going on under the hood. Furthermore, developers have to program around the super slow write rate. Hiding a slow Megastore write behind an asynchronous Javascript call sort of defeats the purpose of having&amp;nbsp;ACID.&lt;/p&gt;
&lt;p&gt;Compared to other Google papers like GFS and MapReduce, Megastore just seems way too complicated. It doesn&#39;t convince me that it&#39;s chosen the right point in the design space, or that it&#39;s fulfilling a particularly pressing need for real applications. I think it&#39;s still interesting to hear about, but I wouldn&#39;t pick this for a 10 year best paper&amp;nbsp;award.&lt;/p&gt;

   </content></entry><entry><title>Paper review: The Google File System</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/gfs_review.html"/><updated>2011-09-24T15:39:00Z</updated><published>2011-09-24T15:39:00Z</published><id>http://www.umbrant.com/blog/2011/gfs_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review for &quot;The Google File System&quot; by Ghemawat et al., published at SOSP in 2003. This is a fairly important paper, and directly inspired the architecture of&amp;nbsp;HDFS.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;What Google did was look very carefully at their desired workload, and build a distributed filesystem specifically for that. GFS is very much not a general purpose filesystem, and I really like how they lay out quite clearly early on the assumptions they&amp;nbsp;make:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Files are almost all large, many&amp;nbsp;GBs&lt;/li&gt;
&lt;li&gt;Target throughput, not&amp;nbsp;latency&lt;/li&gt;
&lt;li&gt;Append-only. Cannot overwrite existing&amp;nbsp;data.&lt;/li&gt;
&lt;li&gt;Must be distributed and&amp;nbsp;fault-tolerant&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem they were basically trying to solve was doing log analytics at scale, meaning mostly long sequential writes of very large files. Spreading files across multiple disks is crucial to getting enough throughput and getting fault-tolerance. GFS can be viewed sort of like a distributed version of RAID&amp;nbsp;1.&lt;/p&gt;
&lt;p&gt;The architecture is a single GFS master which stores metadata for all the files, and a lot of chunkservers that store chunks (64MB) of files. The master is used to chunk locations for a given range of a file, the actual reads and writes are done by directly accessing the appropriate chunkserver. All chunks are replicated across multiple chunkservers for durability and load balancing. Chunkservers talk to the master via heartbeat messages, upon which the master can piggyback commands like re-replicating or getting chunk&amp;nbsp;lists.&lt;/p&gt;
&lt;p&gt;Data consistency is made a lot easier by not having to worry about overwrites. It also means clients can cache chunk locations, since they change&amp;nbsp;rarely.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;relevance&lt;/h3&gt;
&lt;p&gt;GFS clearly does a good job at the application it was designed for: sequential reads for large files by data-parallel workloads. However, since HDFS has become sort of an industry standard for storing large amounts of data, it&#39;s increasingly being used for other types of workloads. HBase is one example of this (a more database-like column store), which definitely does a lot more random I/Os. Facebook also published a paper on doing real-time queries with MapReduce (and thus HDFS). The question is how well HDFS can be squeezed into these roles, and if other storage systems are necessary. For low-latency web serving this is definitely true (memcached and other k-v stores&amp;nbsp;dominate).&lt;/p&gt;
&lt;p&gt;In short, I don&#39;t think the MapReduce paradigm is going anywhere, and HDFS already feels like the standard answer to storing big data. I don&#39;t think it&#39;s going anywhere in the next&amp;nbsp;decade.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Paxos, Paxos, and Chubby</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/paxos_papers_review.html"/><updated>2011-09-22T14:53:00Z</updated><published>2011-09-22T14:53:00Z</published><id>http://www.umbrant.com/blog/2011/paxos_papers_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I&#39;m combining my paper reviews this time, since they are pretty closely&amp;nbsp;coupled:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Paxos Made Simple&quot;, Lamport,&amp;nbsp;2001&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Paxos Made Practical&quot;, Mazieres,&amp;nbsp;2007&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;The Chubby Lock Service for Loosely-coupled Distributed Systems&quot;, Burrows,&amp;nbsp;2006&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Paxos Made&amp;nbsp;Simple&lt;/h3&gt;
&lt;p&gt;Paxos is a distributed consensus algorithm. At it&#39;s essence, it&#39;s a quorum-based fault-tolerant way of arriving at a single consistent value among a group of machines. This can be used to do leader election (consensus on who&#39;s the master), or synchronous strong consistency (replicating writes in a distributed database). In the case of Chubby, it&#39;s used for both: determining who holds a lock (leader election), and serving strongly consistent small&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;Paxos has two basic types of&amp;nbsp;actors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Proposers&lt;/em&gt; are nodes that are trying to get a value accepted as the &quot;true&quot; value during a round of consensus. Normally, which node is the proposer is pretty stable, and only changes on&amp;nbsp;failure.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Acceptors&lt;/em&gt; receive proposals from proposers, and vote as part of the quorum. They act as replicas for the global state of the&amp;nbsp;system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Paxos also has two rounds of communication. Here I&#39;m showing the basic version. There are four types of messages here: propose, promise, accept, and&amp;nbsp;accepted.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Propose&lt;ul&gt;
&lt;li&gt;The proposer sends a &lt;em&gt;proposal&lt;/em&gt; to all the acceptors in the system, and waits for a reply. This proposal is tagged with a round number &lt;strong&gt;N&lt;/strong&gt;, which has to increase each time this proposer makes a new proposal. A common thing to do is make this number out of the IP address and some local counter, so they are globally&amp;nbsp;unique.&lt;/li&gt;
&lt;li&gt;The acceptors &lt;em&gt;promise&lt;/em&gt; to accept the proposal if the proposal&#39;s &lt;strong&gt;N&lt;/strong&gt; is the highest &lt;strong&gt;N&lt;/strong&gt; they&#39;ve seen from any proposer. A promise indicates that the acceptor will ignore any proposal with a lower number. This is so there&#39;s a total ordering on proposals; we don&#39;t care which proposer wins, we just want one to&amp;nbsp;win. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Accept&lt;ul&gt;
&lt;li&gt;If the proposer hears back from a quorum of acceptors, it sends out &lt;em&gt;accept&lt;/em&gt; requests to the quorum with the value it wants to&amp;nbsp;set.&lt;/li&gt;
&lt;li&gt;If the acceptor hasn&#39;t promised to a higher proposal number in the meantime, it tells the proposer it &lt;em&gt;accepted&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the proposer gets back a quorum of accepted messages, then it commits. At this point, it can further replicate the value to other nodes, or respond to the client that the operation worked. It&#39;s normal for all nodes in the system to in fact fulfill the roles of both proposer and acceptor as&amp;nbsp;needed.&lt;/p&gt;
&lt;p&gt;There is one detail that prevents future proposers from changing the value when issuing a higher number proposal. In the first round, the acceptor promise also sends the number and value of the highest number proposal they&#39;ve accepted. Then, the proposer uses the value of the highest number proposal it gets back when it commits in the accept&amp;nbsp;round.&lt;/p&gt;
&lt;h3&gt;Paxos Made&amp;nbsp;Practical&lt;/h3&gt;
&lt;p&gt;This paper goes into a lot of detail about how to actually implement Paxos, handling nodes leaving and adding a Paxos replicated state machine (ala an RPC server). All nodes run the same deterministic code which transitions based on input. Non-deterministic bits are decided by a single machine externally before being passed into the state machine. One of the machines act as the master (proposer) in Paxos operations, handling a client request, running Paxos, and executing and responding to the client after a majority of nodes have logged the operation. Changes in group membership (the view) are handled by again running Paxos to agree on&amp;nbsp;membership.&lt;/p&gt;
&lt;h3&gt;Chubby Lock&amp;nbsp;Service&lt;/h3&gt;
&lt;p&gt;This is an engineering industry paper from Google, always interesting reads. The basic idea of Chubby is running Paxos on a small &lt;em&gt;cell&lt;/em&gt; of five machines to solve consensus problems. One of the five machines in the cell acts as the master, and runs Paxos to decide writes. Reads are all served from the master. This lets Chubby provide &lt;em&gt;coarse-grained&lt;/em&gt; locking services, where locks are expected to be held for long periods of time (minutes, hours, days). Finer grained locking is deferred to application-level servers. Chubby&#39;s API is through a simple way Unix-like filesystem, upon which clients can open file handles and get and release reader/writer locks. This is also convenient for advertising results, and can be used to store small files in a very consistent manner. A typical use within Google is using it as a better version of DNS (no worrying about stale entries and&amp;nbsp;TTLs).&lt;/p&gt;
&lt;p&gt;The rest of the paper describes other features of Chubby: failover, cache consistency, event notifications. Master failover is handled by leader election in the Chubby cell, with a new node brought online in the background. Client caching is also an essential part of reducing load on the Chubby master, but the master has to synchronously invalidate caches on writes. Invalidation is piggybacked on heartbeat &lt;em&gt;KeepAlive&lt;/em&gt; messages (default every 12 seconds), a lease mechanism that keeps client locks and cache alive. Event notifications can also be used to watch files for certain events: modification, master failover, etc. This is most often used to wait for modifications to a file, indicating that a new leader has locked and written its address to the&amp;nbsp;file.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;trends&lt;/h3&gt;
&lt;p&gt;Seeing how this is being used to great extent at Google, it&#39;s got great future applicability. Chubby seems to be used mostly for inter-datacenter locking, I wonder if there are any important modifications that have to be done for intra-datacenter locking. The whole space of eventual consistency gives a lot of alternatives to the strong guarantees that Paxos offers, so there are lots of ways to tradeoff availability and performance with&amp;nbsp;consistency.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Cluster-Based Scalable Network Services</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/cluster_based_scalable_network_services_review.html"/><updated>2011-09-22T14:01:00Z</updated><published>2011-09-22T14:01:00Z</published><id>http://www.umbrant.com/blog/2011/cluster_based_scalable_network_services_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Cluster-Based Scalable Network Services&quot; by Fox et al., published at SOSP in 1997. It describes an architecture for datacenter services that proved to be prescient, and used the Inktomi as an&amp;nbsp;example.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h2&gt;Main&amp;nbsp;ideas&lt;/h2&gt;
&lt;p&gt;This paper has to be put into context. At this point there was still contention whether &quot;clusters of workstations&quot; was the right approach for handling web-sized workloads. Inktomi was at the forefront of saying that yes, clusters were the right choice, and this paper demonstrates why this is true, and how datacenter services can be structured to achieve their key goals: &lt;em&gt;scalability&lt;/em&gt;, &lt;em&gt;availability&lt;/em&gt;, and &lt;em&gt;cost effectiveness&lt;/em&gt; by using consistency semantics weaker than ACID:&amp;nbsp;BASE.&lt;/p&gt;
&lt;p&gt;The advantages of clusters are manyfold. They allow easy incremental scaling and upgrading, they can be build out of commodity parts, and they have natural redundancy through replication. Disadvantages are primarily in the programming model and management; it can be difficult to harness a group of machines to complete a task, and since it&#39;s a distributed system, there are issues with data consistency and&amp;nbsp;failures.&lt;/p&gt;
&lt;p&gt;The idea of BASE is a crucial component of this. BASE stands for &lt;strong&gt;B&lt;/strong&gt;asically &lt;strong&gt;A&lt;/strong&gt;vailable, &lt;strong&gt;S&lt;/strong&gt;oft state, &lt;strong&gt;E&lt;/strong&gt;ventual consistency. This is a significant relaxation of strict ACID semantics, since it allows servers to temporarily serve stale data while state converges. This is allows better performance, and many applications do not require strict ACID semantics to provide a good user&amp;nbsp;experience.&lt;/p&gt;
&lt;p&gt;The cluster architecture proposed also looks shockingly similar to what is in use today. Within a datacenter, machines are split into two major groups: front-end and workers. Front-ends handle actual client requests from outside the datacenter. To handle a client request, a front-end might harness a number of workers running different services to get data or do computation, before assembling and returning the response. This allows all the front-ends to share from the same pool of stateless workers which is good for utilization, and also allows pools of workers to be scaled up and down in response to&amp;nbsp;overload.&lt;/p&gt;
&lt;h2&gt;Future trends and&amp;nbsp;relevance&lt;/h2&gt;
&lt;p&gt;Seeing how Brewer wrote this paper in 1997 and we&#39;re still using roughly the same architecture today in 2011, I don&#39;t think there&#39;s any doubt that the paper had a lot of future relevance. I think there&#39;s still room for improvement in the cluster management side of things (Mesos), but the idea of clusters for datacenters has reached complete acceptance. Interestingly though, we&#39;re seeing the return of &quot;big iron&quot; to the datacenter for some applications. People are starting to wonder about the possibilities offered by a machine with 1TB of memory (purchasable today), and the &quot;disk is tape, memory is disk&quot; argument along with a strong focus on latency might lead to further development on the cluster programming model front. SSDs present yet another level in the storage hierarchy with unique cost and performance&amp;nbsp;tradeoffs.&lt;/p&gt;

   </content></entry><entry><title>Paper review: The Datacenter Needs an Operating System</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/datacenter_needs_an_operating_system_review.html"/><updated>2011-09-14T11:40:00Z</updated><published>2011-09-14T11:40:00Z</published><id>http://www.umbrant.com/blog/2011/datacenter_needs_an_operating_system_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;The Datacenter Needs an Operating System&quot; by Zaharia et al. This is a short 5 page paper published at HotCloud&amp;nbsp;2011.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;This is a high-level ideas paper focusing on the abstractions that should be provided in a cluster programming environment. The authors identify the following as the core traits of traditional operating&amp;nbsp;systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resource&amp;nbsp;sharing&lt;/li&gt;
&lt;li&gt;Data sharing between&amp;nbsp;programs&lt;/li&gt;
&lt;li&gt;Programming abstractions for software&amp;nbsp;development&lt;/li&gt;
&lt;li&gt;Debugging and&amp;nbsp;monitoring&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The authors argue that these same things should be provided to cluster applications as a common layer, instead of having each programming paradigm separately implement them in an adhoc fashion. If I interpret the article correctly, they want to provide a common set of abstractions on top of which programming models like MapReduce or Dryad can be built, benefiting from code sharing as well as more efficient&amp;nbsp;utilization.&lt;/p&gt;
&lt;h3&gt;Problems&lt;/h3&gt;
&lt;p&gt;What I really would have liked to have seen (perhaps in a longer paper) is more of a focus on where the abstractions are going to be drawn between the &quot;datacenter OS&quot; and the &quot;datacenter application&quot;. This is a classic problem in traditional OS literature (should it be in the kernel, or in user space?), and I&#39;m betting we&#39;ll see the same theme being explored here. Since all these different frameworks already exist, the initial question is figuring out what can actually be pushed down into the&amp;nbsp;OS.&lt;/p&gt;
&lt;p&gt;Debugging is one issue raised that none of the computing frameworks have really solved, so that&#39;s the least-defined problem on the list in my mind. Scheduling and resource sharing are difficult, but&amp;nbsp;approachable.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;impact&lt;/h3&gt;
&lt;p&gt;Highly relevant. A proper datacenter operating system enables higher utilization, better performance, and an easier programming environment. As stated before, cloud computing is only becoming more prevalent and&amp;nbsp;important.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Performance Modeling and Analysis of Flash-based Storage Devices</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/performance_modeling_and_analysis_of_flash_review.html"/><updated>2011-09-12T23:43:00Z</updated><published>2011-09-12T23:43:00Z</published><id>http://www.umbrant.com/blog/2011/performance_modeling_and_analysis_of_flash_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Performance Modeling and Analysis of Flash-based Storage Devices&quot; by Huang et al. This paper compares and contrasts three different types of drives: a high-end Intel SSD, a low-end Samsung SSD, and a 5400 RPM Samsung HDD. My review focuses on the implications for cloud&amp;nbsp;computing.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;Solid state disks have to be treated kind of like black boxes when it comes to performance modeling. As is evident from comparing the Intel and Samsung SSDs, we can get wildly different performance characteristics depending on how the manufacturer has configured the Flash Translate Layer (FTL), which maps block requests to actual storage locations. This is important because the FTL acts as a pseudo-filesystem, and has the ability to decide how data is really laid out on the drive independent of the ordering presented to the operating system. This has deep implications for filesystem design (don&#39;t bother minimizing seeks with reordering), and other characteristics of SSDs mean that coalescing and batching requests simply aren&#39;t as&amp;nbsp;effective.&lt;/p&gt;
&lt;p&gt;It seems really worthwhile for SSD vendors to invest in better FTL code, since the high-end Intel SSD appears almost unaffected performance-wise by the randomness of its workload. This indicates to me that it&#39;s probably doing LFS-like block placement to try and minimize the number of writes and erases. This is in stark contrast to the Samsung SSD, which sees its random write performance get trounced by&amp;nbsp;sequential.&lt;/p&gt;
&lt;p&gt;I think there&#39;s definitely space for SSDs in the storage hierarchy on the cloud. It&#39;s safe to assume future SSDs will have FTL firmware with performance similar to the Intel SSD, so we&#39;re basically gaining a tier that has strictly better performance characteristics than hard disk drives. It won&#39;t displace HDDs entirely since the price/GB is still way higher and the amount of data is only growing, but it does sit sort of nicely between memory and HDDs. There&#39;s less of a value argument for MapReduce (an application designed specifically to make good use of HDDs by doing large sequential reads), but for more random workloads (thinking of OLTP, or web caches) SSDs will be a big win. The limitations on the number of write cycles could be a problem, but can be countered through schemes like unbalancing writes to a single SSD in an array (making it fail first reliably, which is better), or by ironically sticking a HDD in front to buffer&amp;nbsp;writes.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Data-Level Parallelism in Vector, SIMD, and GPU Architectures</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/data-level_parallelism_review.html"/><updated>2011-09-12T23:21:00Z</updated><published>2011-09-12T23:21:00Z</published><id>http://www.umbrant.com/blog/2011/data-level_parallelism_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a review of Chapter 4 from the Hennessy and Patterson book, &quot;Computer Architecture, 5th Edition: A Quantitative Approach&quot;. This chapter covers the differences between vector processors, SIMD, and GPU architectures. This writeup focuses entirely on the future hardware trends of data-parallel SIMD hardware in the&amp;nbsp;cloud.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;My claim is that we&#39;re going to see GPUs moving on-chip to coexist with normal CPU cores; we&#39;re already seeing this happen with the newest processors from Intel and AMD. The reason for this is that the latency cost in shifting computation to a GPU over PCI-E is high, and there isn&#39;t any memory coherency. Moving GPU cores on-chip solves both of these problems, though memory coherency still faces the same problems it does among many CPU cores. I can&#39;t make any solid predictions about vector processors, but it seems like the momentum is heavily in favor of GPU and SIMD CPU extensions for data-parallel&amp;nbsp;computation.&lt;/p&gt;
&lt;p&gt;How does this relate to cloud computing? GPUs face one major problem in a cloud environment, in that they are currently quite hard to virtualize. Preemption support is nascent, and there&#39;s a high cost to context switching a GPU because of the high latency bus. Concurrent sharing might also be difficult. There also aren&#39;t well-defined standards for programming a GPU (CUDA and OpenCL being competing&amp;nbsp;examples).&lt;/p&gt;
&lt;p&gt;Putting all of the problems aside as solvable however, GPUs are great for speeding up data-parallel tasks (and GPUs already can run MapReduce). I definitely see them gaining traction for batch processing. For normal web-serving workloads though, I&#39;m not sure where a GPU would be useful. I think it&#39;s a lot harder to derive data-parallelism from handling a single request, and the current limitations on multiplexing and latency make it less friendly for this type of&amp;nbsp;work.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Amdahl&#39;s Law in the Multicore Era</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/amdahls_law_review.html"/><updated>2011-09-11T19:08:00Z</updated><published>2011-09-11T19:08:00Z</published><id>http://www.umbrant.com/blog/2011/amdahls_law_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a paper review of &quot;Amdahl&#39;s Law in the Multicore Era&quot;, by Hill and&amp;nbsp;Marty.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;There are two important equations in this paper that lay the foundation for the rest of the paper: Amdahl&#39;s law, and the processor performance&amp;nbsp;law.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;speedup = 1 / ((1-f + f/s)
perf(r) = sqrt(r)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These two equations have some deep implications. Starting with the latter, there are diminishing returns from investing more chip resources to single core performance. It&#39;s the reason why per-core performance has basically peaked in recent years, and everything is moving toward multicore. Adding more cache and beefing up adders can only do so much; since clock rates have peaked because of power limitations, we&#39;re basically stuck with the per-core performance we&#39;ve&amp;nbsp;got.&lt;/p&gt;
&lt;p&gt;Moving on to Amdahl&#39;s law, &lt;code&gt;f&lt;/code&gt; is the fraction of a program that is parallelizable, and &lt;code&gt;s&lt;/code&gt; is the speedup of the parallelizable part. Interpreting this, we see there are essentially three ways of making your program run&amp;nbsp;faster:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run the serial part faster (&lt;code&gt;1-f&lt;/code&gt; component)&lt;/li&gt;
&lt;li&gt;Make &lt;code&gt;s&lt;/code&gt; bigger (increase the granularity of&amp;nbsp;parallelism)&lt;/li&gt;
&lt;li&gt;Make &lt;code&gt;f&lt;/code&gt; bigger (parallelize serial parts of the&amp;nbsp;code)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Combining both equations, what are the limitations we&amp;nbsp;see?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Running the serial part faster gets expensive quickly in terms of chip&amp;nbsp;resources.&lt;/li&gt;
&lt;li&gt;Increasing the granularity of parallelism is a great approach, but only works really well for data-parallel tasks like serving webpages. Going beyond this natural parallelism into true fine-grained parallelism is expensive and&amp;nbsp;limited.&lt;/li&gt;
&lt;li&gt;Parallelizing serial parts of the code is also a great approach, but similarly limited. Some serial code simply can&#39;t be parallelized, and there&#39;s the same diminishing returns effect in terms of programmer&amp;nbsp;time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The article goes on to talk about different ways of allocating chip resources in light of these two laws, covering the symmetric, asymmetric, and dynamic multicore chips. Basically, favoring bigger cores makes the serial part run faster, but reduces the number of ways you can split the parallel part, with smaller cores having the opposite effect. 
The net result of the paper is that it all depends on your workload. Asymmetric and dynamic multicore offer better performance characteristics than symmetric since they can better handle both the serial and parallel parts of a program&amp;nbsp;well.&lt;/p&gt;
&lt;p&gt;Applying this to cloud computing, we see that there are a lot of parallels between multicore and cloud computing. Instead of getting speedup by parallelizing at the level of instructions in a program, web services are typically scaled through request-level parallelism: distributing requests among a cluster of machines. In this case, the &lt;code&gt;1-f&lt;/code&gt; factor in Amdahl&#39;s law can be effectively zero, since requests can be handled independently by each&amp;nbsp;machine.&lt;/p&gt;
&lt;p&gt;The ideas of asymmetric cores is also already seen in request-level parallelism since bigger tasks can be allocated to bigger&amp;nbsp;machines.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;trends&lt;/h3&gt;
&lt;p&gt;Servers processors are going to be manycore in the future, whether we want it or not. Peak per-core performance is probably not going to increase, and I don&#39;t hold out much hope for the effectiveness of dynamic multicore, but I think there&#39;s a strong argument for asymmetric multicore since there are demonstrated benefits over symmetric. Big cores will be used for serial computation, with data-parallel parts off-loaded to small cores as possible. This is also better for the coming scarcity of memory bandwidth; having big cores that can use it more effectively makes&amp;nbsp;sense.&lt;/p&gt;
&lt;p&gt;This also lends flexibility into request handling, since high-priority tasks can get the big core while latency-tolerant operations can run on a small core. The big problem here is going to be performance isolation for shared resources like mem, disk, and network; with more threads, there&#39;s more contention, and potentially more variability in performance (which is the killer). Scheduling also becomes more difficult, since &quot;slots&quot; on the same machine are now unequal in&amp;nbsp;size.&lt;/p&gt;
&lt;p&gt;For batch processing (like Hadoop), throughput matters more than latency. Here, SMP is the name of the game: we want to optimize &lt;code&gt;perf(r)&lt;/code&gt; and have a bunch of wimpy cores. I still worry about I/O demands on durable storage, since HDD throughput doesn&#39;t seem to be increasing at the same rate that cores are being&amp;nbsp;added.&lt;/p&gt;
&lt;p&gt;Software stack&amp;nbsp;predictions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We&#39;re going to see a focus on better asymmetric-aware, load-based cluster scheduling algorithms. This needs to balance out I/O load&amp;nbsp;too.&lt;/li&gt;
&lt;li&gt;We&#39;re going to see moderate fine-grained parallelism added to existing request-level parallelism, since it&#39;s the real only way of reducing&amp;nbsp;latency. &lt;/li&gt;
&lt;li&gt;As a corollary, I think there&#39;s going to be a big focus on performance isolation even at the cost of throughput, since variation in latency is the real&amp;nbsp;killer.&lt;/li&gt;
&lt;li&gt;MapReduce as a programming model and framework probably will not go away, but we&#39;re going to see mappers making better use of multi-core. Whether this is going to be MapReduce-in-MapReduce or OpenMP-in-MapReduce or Pthreads-in-MapReduce is unclear, but it makes sense to further break down an already data-parallel task into core-sized&amp;nbsp;chunks.&lt;/li&gt;
&lt;/ul&gt;

   </content></entry><entry><title>Paper review: The Datacenter as a Computer Ch. 3, 4, 7</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/datacenter_as_a_computer_3_4_7_review.html"/><updated>2011-09-08T20:31:00Z</updated><published>2011-09-08T20:31:00Z</published><id>http://www.umbrant.com/blog/2011/datacenter_as_a_computer_3_4_7_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a review of chapters 3, 4, and 7 from &quot;The Datacenter as a Computer&quot;. The topics covered are hardware for servers, datacenter basics, and fault-tolerance and&amp;nbsp;recovery. &lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;idea&lt;/h3&gt;
&lt;p&gt;The three chapters here cover essentially how to design the hardware, software, and operational concerns of a&amp;nbsp;datacenter. &lt;/p&gt;
&lt;p&gt;In terms of datacenter hardware, the main concern is choosing the most cost-efficient type of node that still runs your workload sufficiently&amp;nbsp;fast.&lt;/p&gt;
&lt;p&gt;Operationally, the concerns deal with power and cooling of nodes, factors which limit the density of nodes in a datacenter. Cooling can account for a major part of the power cost of a datacenter, and AC is just as critical a service as power since the datacenter can survive for only a matter of minutes if the AC unit&amp;nbsp;dies.&lt;/p&gt;
&lt;p&gt;The chapter on fault-tolerance and recovery talks about the different types of faults that can present in hardware and software, and how they might affect service availability. The ultimate goal of the service is to be able to survive faults without significantly affecting availability, either through overprovisioning or graceful degradation of service&amp;nbsp;quality.&lt;/p&gt;
&lt;h3&gt;Problems&amp;nbsp;presented&lt;/h3&gt;
&lt;p&gt;I divided this up into three sections, based on the&amp;nbsp;chapters.&lt;/p&gt;
&lt;h4&gt;Hardware/software&amp;nbsp;scaling&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;How low can you go? Small nodes are more cost-efficient than beefy ones in terms of computation-per-watt and price-per-computation, but might start becoming a bottleneck due to the limits of request parallelism. Further parallelization of an application can be really painful, and having to deal with coordination between more nodes has its drawbacks. Never forget Amdahl&#39;s Law: if the serial parts of your program dominate, and you&#39;re running that serial code on slow nodes, your program is going to run slowly too. Beefy nodes can run the serial part&amp;nbsp;quickly.&lt;/li&gt;
&lt;li&gt;Another point about small nodes is that they are harder to schedule efficiently. Resources effectively get fragmented; there might not be enough left on a small node to schedule a new task. Big nodes pack more&amp;nbsp;efficiently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Operations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;What is the most effective way of cooling servers? This is heavily related to power density (# of servers / volume), with a higher power density being a more efficient use of a datacenter. The current canonical strategy seems to be alternating hot and cold aisles, with cold air pumped up through the floor by a central AC unit. Modular container datacenters seem to be another strategy, which are effective because they can be designed very tightly and are&amp;nbsp;self-contained.&lt;/li&gt;
&lt;li&gt;What is the most efficient way of cooling server? This relates to economic costs; it&#39;s claimed that cooling can account for 40% of load, which is a big power&amp;nbsp;bill.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One question I had here was that they mention that UPSes are generally in a separate room, with just the power distribution unit on the floor. I remember reading that Google integrated batteries right into their servers, but maybe one source or the other is outdated. Having a battery right in the server might increase fault-tolerance and&amp;nbsp;modularity.&lt;/p&gt;
&lt;h4&gt;Fault-tolerance&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;What are the real limitations on availability? Internet is apparently only 99.99% available, and software faults dominate hardware faults (only 10%). Machines apparently last an average of 6 years before replacement, after factoring out &quot;infant mortality&quot;, a number I found to be surprisingly high considering how much talk there is of nodes keeling over, but again just indicates that most keeling is due to software&amp;nbsp;faults.&lt;/li&gt;
&lt;li&gt;How do you maintain service when faults happen? In a datacenter, a new node fails on the order of hours, so it&#39;s not acceptable to become unavailable. This is done through some degree of overprovisioning of resources, and designing software that is fault-tolerant. When operating with faults, it&#39;s desirable to have the property of &lt;em&gt;graceful degradation&lt;/em&gt;, where the quality of service gradually degrades (e.g. using older cached data, serving reads but denying writes, disabling some features). Fault-tolerance also makes the need for repairs less urgent and thus less&amp;nbsp;expensive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tradeoffs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Programming cost vs. hardware cost. Can speed up either by throwing programmers at a problem (squeeze more parallelism from the code), or by buying better hardware (run the same code faster). This is an economic balancing&amp;nbsp;act.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;This textbook isn&#39;t going to be winning any awards, but I find it to be a fascinating look into operations and system design at Google. Like I mentioned in my last writeup, there are only going to be more datacenters being built in the future, so advice like this on how to design and build datacenters and datacenter applications is very&amp;nbsp;useful.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Warehouse-Scale Computing: Entering the Teenage Decade</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/warehouse_scale_computing_summary.html"/><updated>2011-09-06T22:10:00Z</updated><published>2011-09-06T22:10:00Z</published><id>http://www.umbrant.com/blog/2011/warehouse_scale_computing_summary.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This review is based on a presentation by Luiz Andre Barroso from Google, titled &quot;Warehouse-Scale Computing: Entering the Teenage Decade&quot;. I believe it was given this past year (2011) at FCRC. I really strongly recommend it, since it talks about the operational issues in running a Google datacenter, and also identifies a lot of the research issues surrounding &quot;warehouse-scale&amp;nbsp;computation&quot;.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Key&amp;nbsp;Points&lt;/h3&gt;
&lt;p&gt;There were a couple problems identified in the talk, some of which have been solved, some of which have not&amp;nbsp;been.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I/O latency variability right now is terrible, with basically all durable storage displaying a long latency tail. Random accesses to spinning disks are slow, flash writes are slow, and these high-latency events muck up the latency for potentially fast&amp;nbsp;events.&lt;/li&gt;
&lt;li&gt;Network I/O suffers a similar problem. Using TCP and interrupts adds orders of magnitude of latency to network requests, making fast network hardware slow again in&amp;nbsp;software.&lt;/li&gt;
&lt;li&gt;Datacenter power efficiency as defined by PUE (Power Usage Effectiveness) has gotten pretty good (&amp;lt;10 percent is used on operational overhead). The real problem now is making better use of servers, to get CPU load up into the 80% range instead of the current&amp;nbsp;30%.&lt;/li&gt;
&lt;li&gt;This leads into the another problem: how do you share all of the resources in a cluster among many different services, while also chopping off the latency&amp;nbsp;tail?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To summarize, there are two big ideas in the talk. First, latency and variation in latency are the key performance metrics for services these days; today&#39;s web-based applications demand both to provide a good user experience. This may require reexamination of a lot of fundamental assumptions about IO. Second, increasing the utilization of resources in a cluster is important from an efficiency and performance standpoint. Server hardware should be a fungible resource that can be easily shared among different&amp;nbsp;services.&lt;/p&gt;
&lt;p&gt;The differences here between warehouse-scale computing and datacenter-scale computing lie in scale and the type of user experience provided. Warehouse-scale computing operates in the many petabyte range, and allows for complete system integration of the hardware, software, power, and cooling of the cluster. The types of services hosted by a warehouse-scale computer are also scaled way up, in terms of the latency requirements and the size of the data that is being&amp;nbsp;crunched.&lt;/p&gt;
&lt;h3&gt;Trade&amp;nbsp;offs&lt;/h3&gt;
&lt;p&gt;One of the key tradeoffs mentioned was between latency and throughput. Most of the software stack for I/O these days is done to optimize throughput, done in response to relatively slow disks or networks. An example of this would be Nagle&#39;s algorithm in TCP; small packets are delayed and batched to be sent in bulk, reducing TCP overhead (fewer bytes need to be sent) but also increasing latency. New technologies like flash and fast networks mean that these assumptions should be&amp;nbsp;reexamined.&lt;/p&gt;
&lt;h3&gt;Long-term&amp;nbsp;impact&lt;/h3&gt;
&lt;p&gt;Web-based services are here to stay, and I feel confident in saying that this is an area that is going to see yet more growth. Large internet companies like Google and Facebook are already dealing with these issues internally, and there are only going to be more warehouse-scale datacenters built. It&#39;s clear that these are hard problems that aren&#39;t going away because of some &lt;em&gt;deus ex machina&lt;/em&gt; like Moore&#39;s Law, so any solutions are likely to have a big&amp;nbsp;impact.&lt;/p&gt;

   </content></entry><entry><title>Lottery and stride scheduling</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/lottery_stride_scheduling.html"/><updated>2011-07-17T02:50:00Z</updated><published>2011-07-17T02:50:00Z</published><id>http://www.umbrant.com/blog/2011/lottery_stride_scheduling.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Today is a shorter post than previous topics, since I didn&#39;t want to lump the last paper (Paxos, ick) in with these. I&#39;m covering lottery and stride scheduling, two very related approaches to doing efficient proportional-share scheduling. I believe this is the canonical way of doing things, since mClock (by Gulati et al., presented at OSDI 2010) used stride scheduling successfully to schedule disk I/O in a&amp;nbsp;hypervisor.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Lottery Scheduling: Flexible Proportional-Share Resource Management&quot;, Waldspurger and Weihl,&amp;nbsp;1994&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Stride Scheduling: Deterministic Proportional-Share Resource Management&quot;, Waldspurger and Weihl,&amp;nbsp;1995&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;The basic problem for proportional-share scheduling is, given a set of processes that have been assigned relative &lt;em&gt;weights&lt;/em&gt; (e.g. 3:2:1), schedule the processes with some kind of quantum such that they all get their assigned proportion of CPU time (e.g. 1/2rd, 1/3rd, 1/6th). A naive way of doing this is to simply schedule each process for &lt;em&gt;weight&lt;/em&gt; number of scheduling quantums, but this penalizes processes that do not use their entire allocation (for instance, if they block on I/O early) and results in unfairness at small timescales (think of a 100:1:1 weighting). It&#39;s also desired that scheduling is responsive to changes in&amp;nbsp;priorities.&lt;/p&gt;
&lt;p&gt;These are some of the practical problems that any good proportional share scheduler has to&amp;nbsp;solve.&lt;/p&gt;
&lt;h3&gt;Lottery&amp;nbsp;Scheduling&lt;/h3&gt;
&lt;p&gt;Waldspurger and Weihl&#39;s first approach is a probabilistically fair one. Processes are assigned a number of &lt;em&gt;tickets&lt;/em&gt; based on their relative weight (bigger weight=more tickets), and the scheduler holds a &lt;em&gt;lottery&lt;/em&gt; each scheduling quantum (choosing a random ticket) where the winner gets scheduled. A quantum is small unit of time, in this case 10ms, which is the smallest unit that the scheduler will assign to a process. This means processes with more tickets get scheduled more often, and over time the actual scheduling should probabilistically approaches the desired relative weighting between the processes with standard deviation proportional to &lt;code&gt;sqrt(n)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This can be implemented by logically storing the number of tickets of each processes in an array, and keeping a running sum of tickets as one traverses the array, advancing until the winning ticket is found. This is an &lt;code&gt;O(n)&lt;/code&gt; operation, but sorting the array in descending order with insertion sort (or, as the authors do it, the &quot;move to front&quot; heuristic) can make this a lot faster. For large n, it&#39;s better to take an approach with a tree and binary search toward the winning leaf node&amp;nbsp;process.&lt;/p&gt;
&lt;p&gt;If a process ends early or runs over its quantum, the size of its ticket pool gets adjusted until it gets scheduled again with a &lt;em&gt;compensation ticket&lt;/em&gt;. This compensation ticket is valued at &lt;code&gt;(1/f)*num_tickets&lt;/code&gt;, where &lt;code&gt;f&lt;/code&gt; is the fraction of its allocated time that it actually used. This will increase or decrease its likelihood of getting scheduled&amp;nbsp;appropriately.&lt;/p&gt;
&lt;p&gt;It&#39;s relatively straightforward to build a hierarchical scheduling system via what the authors term &lt;em&gt;ticket currencies&lt;/em&gt;: different types of tickets that are backed by other tickets. In this way, groups of processes can be weighted based on one currency, and then the members of each group weighted based on another currency. This is basically just a nice management feature; in the end, everything gets translated back into the base&amp;nbsp;currency.&lt;/p&gt;
&lt;p&gt;They also have this idea of letting clients pass their ticket allocations off to a server, which is kind of cool when combined with a microkernel where everything is a server. It means you can give tickets only to clients, and make it so all server requests have to be paid for with a ticket allocation. Then, tickets accurately capture all work done in the system on behalf of a client process. My random&amp;nbsp;idea.&lt;/p&gt;
&lt;p&gt;They also have a similar idea with a priority inheritance scheme of sorts for locks, where all the tickets of processes waiting on a lock are passed to the process currently holding the lock. They also test lottery scheduling lock acquisition, which seems of somewhat lower utility to me. I don&#39;t know how often I would ever want to use this, as a&amp;nbsp;programmer.&lt;/p&gt;
&lt;p&gt;Lottery scheduling isn&#39;t really that responsive to dynamic changes, since it takes time to converge. Its probabilistic nature also means that it can&#39;t really give predictable performance, meaning that it might be decently suited for &quot;average throughput&quot; schedulers, but probably sucks for interactive uses where you want SLA-like performance&amp;nbsp;guarantees.&lt;/p&gt;
&lt;h3&gt;Stride&amp;nbsp;Scheduling&lt;/h3&gt;
&lt;p&gt;The follow up paper the year after solves many of the problems with lottery scheduling, by introducing the concept of &lt;em&gt;stride scheduling&lt;/em&gt;, which has all the benefits of lottery scheduling while also being deterministic, responsive to dynamic priorities, and better error properties. This is slightly different from how it&#39;s presented in the paper, but I think it makes more&amp;nbsp;sense.&lt;/p&gt;
&lt;p&gt;This is something most easily explained visually, but the basic idea is the concept of &lt;em&gt;virtual time&lt;/em&gt;, where each process has a clock that ticks at a different rate depending on its priority. High priority clocks tick slowly, while low priority clocks tick quickly. The rate at which a clock ticks is called its &lt;em&gt;stride&lt;/em&gt;. The scheduler makes decisions by finding the clock with the oldest time, scheduling the corresponding process, and then advancing the clock by the clock&#39;s stride. This is implemented simply by keeping the clocks in ascending sorted order via a heap, insertion sort, or something like&amp;nbsp;that.&lt;/p&gt;
&lt;p&gt;Fractional quantums are handled via a multiplicative compensation factor: simply multiplying the stride by the fraction of the quantum used, before advancing the local&amp;nbsp;clock.&lt;/p&gt;
&lt;p&gt;There is also the concept of &lt;em&gt;global virtual time&lt;/em&gt; that advances at the rate of the slowest possible tick (&lt;code&gt;~1/sum(tickets)&lt;/code&gt;). This is used to calculate a compensation factor, &lt;code&gt;remain&lt;/code&gt;, used to compensate a process for time spent waiting when there is a dynamic change. &lt;code&gt;remain&lt;/code&gt; is the amount of virtual time until a process would next be scheduled, i.e. the difference between the global virtual time and the local virtual time. When the process re-enters the system, its local time is set to &lt;code&gt;global_time+remain&lt;/code&gt;. In this way, if the process waited to be allocated before leaving (&lt;code&gt;remain&amp;amp;lt;stride&lt;/code&gt;) it gets scheduled sooner. The opposite happens if the process previously got an early&amp;nbsp;allocation.&lt;/p&gt;
&lt;p&gt;Since the stride and &lt;code&gt;remain&lt;/code&gt; are both related to the size of the ticket allocation, in the case of a dynamic change, the stride is recomputed and used to scale &lt;code&gt;remain&lt;/code&gt; appropriately to immediate reflect the new&amp;nbsp;allocation. &lt;/p&gt;
&lt;p&gt;This is extremely predictable since scheduling is deterministic, and processes are guaranteed to be scheduled at least once every complete cycle of virtual time (where a cycle is the slowest stride). This is a major boon for responsiveness since we no longer have to wait for probabilities to converge. Glancing at the evaluation section, I see major improvements to predictability, responsiveness to priority changes, and accuracy. The same ideas of ticket currencies and ticket passing also apply to stride&amp;nbsp;scheduling.&lt;/p&gt;

   </content></entry><entry><title>Concurrency review</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/concurrency.html"/><updated>2011-07-08T19:14:00Z</updated><published>2011-07-08T19:14:00Z</published><id>http://www.umbrant.com/blog/2011/concurrency.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I assume that everyone has already read Andrew Birrell&#39;s &lt;a href=&quot;ftp://apotheca.hpl.hp.com/pub/dec/SRC/research-reports/SRC-035.pdf&quot;&gt;seminal paper on &quot;Programming with Threads&quot;&lt;/a&gt; or at least has a basic conception of parallel programming. This is going to deal with locking and concurrency at a higher level. At-bat today are five selected papers on&amp;nbsp;concurrency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Granularity of Locks and Degrees of Consistency in a Shared Data Base&quot;, by Gray et al.,&amp;nbsp;1975&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Experience with Processes and Monitors in Mesa&quot;, Lampson and Redell,&amp;nbsp;1980&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;On Optimistic Methods for Concurrency Control&quot;, Kung and Robinson,&amp;nbsp;1981&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Threads and Input/Output in the Synthesis Kernel&quot;, Massalin and Pa,&amp;nbsp;1989&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;dquo&quot;&gt;&quot;&lt;/span&gt;Concurrency Control Performance Modeling: Alternatives and Implications&quot;, Agrawal, Carey and Livny,&amp;nbsp;1987&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;I know I said I expected Birrell&#39;s paper as base knowledge, but here&#39;s a TLDR that might let you skip&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;The need for locking is derived from the concurrent reader/writer problem. It&#39;s safe for multiple threads to be reading the same data at the same time, but it&#39;s not safe to read or write while someone else is writing since you can get corrupted results. This requires the idea of the &lt;a href=&quot;http://en.wikipedia.org/wiki/Readers-writer_lock&quot;&gt;reader-writer lock&lt;/a&gt;, which allows any number of concurrent readers, but will make sure that any writer gets exclusive access (i.e. no other readers or writers are accessing the protected data). This is also called shared/exclusive locking, and is an especially common construct in parallel&amp;nbsp;programming.&lt;/p&gt;
&lt;h3&gt;Granularity of&amp;nbsp;Locks&lt;/h3&gt;
&lt;p&gt;The important takeaway from this Jim Gray paper is the idea of &lt;em&gt;hierarchal locking&lt;/em&gt;, where locking a database table also locks all the rows and row fields in that table. This hierarchal structure allows locking at an almost arbitrary granularity depending on the needs of the executing query, which ameliorates the issues that can happen with too-fine-grained locking (excessive lock overhead from doing lots of acquisitions and releases) or too-coarse-grained locking (poor concurrency from unnecessary lock&amp;nbsp;contention). &lt;/p&gt;
&lt;p&gt;This scheme applies to exclusive (X) locks used for writes as well as share (S) locks used for reads, but also requires the introduction of a third lock type: &lt;em&gt;intention locks&lt;/em&gt;. Intention locks are used to indicate in an ancestor node that one of its children has been locked, preventing another query from falsely locking the ancestor, and thus, the child as well. This is refined to having both a &lt;em&gt;intention share lock&lt;/em&gt; (IS) and an &lt;em&gt;intention exclusive lock&lt;/em&gt; (IX) to allow concurrent reads, since intention share locks are compatible. Exclusive intention locks are also compatible, since they still have to ultimately exclusively lock the child they want to modify. Queries are required to leave a breadcrumb trail of correct intention locks behind as they traverse toward what they ultimately want to access. Locks also must be released in leaf-to-root order, so the locking hierarchy remains&amp;nbsp;consistent.&lt;/p&gt;
&lt;p&gt;One more intention lock type is introduced for yet better concurrency: &lt;em&gt;share and intention exclusive locks&lt;/em&gt; (SIX). This is interpreted as &quot;read-only access to a subtree, exclusively-locking some to be written&quot;. This is necessary because normally you can&#39;t have concurrent read/writes (cannot just first acquire the share lock and then an intention exclusive lock since they&#39;re incompatible), but since these rights are being granted to the same query, it can be depended upon not to read a row that it&#39;s currently writing. This read-modify-write behavior for a subtree is super common in databases, which is why SIX locks are&amp;nbsp;important.&lt;/p&gt;
&lt;p&gt;Table 1 on page 5 of the paper is a concise rundown of what locks are compatible with each other. It might be a nice exercise to work through (the lock types being null, IS, IX, S, SIX,&amp;nbsp;X).&lt;/p&gt;
&lt;p&gt;The rest of the paper seems less relevant. Gray et al. explain how this can be applied to a more dynamic graph based on locking ranges of mutable indexes, with the same strategy. I don&#39;t think this works for multiple indexes, since then the hierarchy DAG is no longer a DAG. They also cover the idea of &quot;degrees of consistency&quot; with tradeoffs between performance (concurrency) and recovery (consistency). I don&#39;t think real-world databases use anything except the highest degree of consistency, since the idea of unrecoverable, non-serializable transactions isn&#39;t pleasant. Anything with eventual consistency (looking at you, NoSQL) has made this&amp;nbsp;tradeoff.&lt;/p&gt;
&lt;h3&gt;Experience with Processes and Monitors in&amp;nbsp;Mesa&lt;/h3&gt;
&lt;p&gt;This paper specifies a new parallel programming language, Mesa, used to implement a new operating system, Pilot. Introducing a new language and OS at the same time is pretty common, and is how we arrived at C and Unix. This is where the idea of &quot;Mesa semantics&quot; for monitors came from (compared to Hoare semantics). To put the work in proper context, apparently in 1979 one had to justify why a preemptive scheduler is required over a non-preemptive design even assuming a uniprocessor (the obvious reason being interrupt&amp;nbsp;handling).&lt;/p&gt;
&lt;p&gt;Mesa is kind of a neat language, in that any procedure can be easily forked off as a new process, and processes are first class values in the language and can be treated like any other value. This isn&#39;t to say that everything is expected to be able to run concurrently, just that the &lt;code&gt;FORK&lt;/code&gt; language construct is easy to apply. The core organizational construct in Mesa is the &quot;module&quot; or the &quot;monitor module&quot;. This is basically a way of logically organizing procedures, and specifying which of them need to acquire the monitor lock as part of&amp;nbsp;execution.&lt;/p&gt;
&lt;p&gt;This is also where &quot;Mesa semantics&quot; come in. Instead of immediately switching to a waiting process on a signal, the signaller continues running. This seems like a great win, since although it means slightly different semantics to the program, it also means fewer context&amp;nbsp;switches.&lt;/p&gt;
&lt;p&gt;The paper goes on to describe more about monitors and the&amp;nbsp;implementation.&lt;/p&gt;
&lt;h3&gt;On Optimistic Methods for Concurrency&amp;nbsp;Control&lt;/h3&gt;
&lt;p&gt;When I read this paper for 262A, it was a big eye-opener. I felt that the idea wouldn&#39;t hold up in real usage (and I think that this is true, except in the specific situations noted), but it was a refreshing approach to handling concurrency I had never thought&amp;nbsp;of.&lt;/p&gt;
&lt;p&gt;The idea behind &quot;optimistic concurrency&quot; is doing away with locking, and instead doing checking at the end before commit to see if there are any conflicts from concurrent queries, and aborting if so. In this way, even if incorrect query results are generated along the way, they are not externalized. This is speculative and will result in lots of aborted transactions (and thus wasted work) under write-heavy workloads, but as the paper says, this works wonderfully for read-heavy workloads where it&#39;s unlikely to have a&amp;nbsp;conflict.&lt;/p&gt;
&lt;p&gt;The motivation here is that locking often imposes unnecessary overhead, and can complicate things. In a locking scheme, even read-only queries need to lock rows even though they aren&#39;t modifying the data, just to indicate that the reads is happening. All this checking and verifying adds up, increasing complexity of the system, and leading to potential deadlocks which have to be resolved through a deadlock-free scheme, or deadlock detection and abort. Locking also can operate at too coarse a granularity; imagine the root node in a hierarchical locking scheme as described by Gray et al., it&#39;s basically constantly under lock contention, often times unnecessarily since queries are not necessarily operating on the same&amp;nbsp;subtrees.&lt;/p&gt;
&lt;p&gt;This is implemented by having &lt;em&gt;two-phase transactions&lt;/em&gt;, which goes &lt;em&gt;read phase&lt;/em&gt;, &lt;em&gt;validate&lt;/em&gt;, then &lt;em&gt;write phase&lt;/em&gt;. In the read phase, the transaction gathers up the names of all the objects it needs to read, defining a &lt;em&gt;read set&lt;/em&gt;. It then validates whether the transaction T_j is &lt;a href=&quot;http://en.wikipedia.org/wiki/Serializability&quot;&gt;serializable&lt;/a&gt;, checking to make sure that for all prior transactions T_i one of the following is&amp;nbsp;true:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;T_i completes its write phase before T_j starts its read phase. T_i comes entirely before T_j, so it&#39;s&amp;nbsp;fine.&lt;/li&gt;
&lt;li&gt;The write set of T_i does not intersect with the read set of T_j, and the T_i finishes writing before T_j starts writing. As long as there&#39;s no intersection, T_j&#39;s reads are safe, and as long as T_i finishes writing before T_j starts, T_j will not be overwritten by&amp;nbsp;T_i.&lt;/li&gt;
&lt;li&gt;The write set of T_i does not intersect the read or write set of T_j, and T_i finishes reading before T_j starts writing. Similar to the previous, no intersection with the read or write set makes T_j very safe, and T_i needs to finish reading before T_j starts writing to protect T_i&#39;s read&amp;nbsp;set.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means that validation needs to check the write sets of all transactions that had finished reading but not finished writing (conditions 2 and 3). This poses an issue for long-running transactions, since the validator might be expected to keep around write-sets almost indefinitely. The proposed answer is to abort and restart the transaction, which leads to the question of how to deal with transactions that repeatedly fail validation. This is answered by write-locking the entire database and letting the &quot;starving&quot; transaction run to completion in isolation. This isn&#39;t great at all, but the assumption is that both long-running transactions and repeat-failures are&amp;nbsp;rare.&lt;/p&gt;
&lt;p&gt;The evaluation in this paper is kind of spotty. It&#39;s purely theoretical, and they chose to do their analysis on a B-tree, which is one of the better (though also common) situations because of the high fanout and low depth leading to lock contention on root nodes. They also assume a uniformly random distribution for accesses which is probably untrue (accesses are normally temporally correlated, which is why LRU caching&amp;nbsp;works).&lt;/p&gt;
&lt;p&gt;All-in-all, this is the system design maxim of &quot;optimize for the common case&quot; taken to the extreme. The common case of no-conflict transactions will be faster with optimistic concurrency control, but it&#39;ll collapse under load a lot worse. Like the authors say, it&#39;s only for situations where transaction conflict is&amp;nbsp;rare.&lt;/p&gt;
&lt;h3&gt;Threads and Input/Output in the Synthesis&amp;nbsp;Kernel&lt;/h3&gt;
&lt;p&gt;This seems to be a more meta paper, where optimistic concurrency and lock-avoiding techniques were applied to an OS to improve performance. It&#39;s also chock-full of system-specific jargon, which I will kindly avoid introducing. Honestly, most of what&#39;s laid out in the paper feels like a bunch of small optimizations for a specialized kernel that add up to something that performs demonstrable better than SunOS. Some of these techniques might be translatable back to Unix-y implementations, some of it is unique to the system (runtime optimization of syscalls?), and some of it is because it&#39;s a special-purpose kernel. I like how the references are to the SunOS source code, GEB (yes, the Hofstadter book), 3 of the author&#39;s own papers, and then two external. Certainly a different&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;The takeaways here are unclear. The conclusion of &quot;avoid synchronization when possible&quot; seems hardly novel, and it feels too much like they implemented to optimize their microbenchmarks (no real apps were&amp;nbsp;written).&lt;/p&gt;
&lt;h3&gt;Concurrency Control Performance&amp;nbsp;Modeling&lt;/h3&gt;
&lt;p&gt;This paper does a deep comparison between three concurrency control algorithms: blocking locks, immediate restart on lock contention, and optimistic concurrency. I really love papers like this one, since they take a bunch of different algorithms that all tested well under different model assumptions, carefully dismantle said assumptions, and reveal real truths with their own meticulous performance model. It really demonstrates the authors&#39; complete understanding of the problem at&amp;nbsp;hand.&lt;/p&gt;
&lt;p&gt;There are a number of model parameters that are crucial to performance here. The &lt;em&gt;database system model&lt;/em&gt; specifies the physical hardware (CPUs and disks), associated schedulers, characteristics of the DB (size and granularity), load control mechanisms, and the concurrency control algorithm itself. The &lt;em&gt;user model&lt;/em&gt; specifies the arrival process for users, and the type of transactions (batch or interactive). The &lt;em&gt;transaction model&lt;/em&gt; specifies the storage access pattern of a transaction, as well as its computational requirements (expressed in terms of&amp;nbsp;probabilities). &lt;/p&gt;
&lt;p&gt;I consider this to be about as complete as possible. They ignore I/O patterns and cache behavior, but those are just damn hard to model. Using a Poisson distribution for transaction inter-arrival rates is canonical without evidence to disprove it ( see &quot;On the Self-similar Nature of Ethernet Traffic&quot; by Leland et al. for a situation where Poisson does not hold so true). They also do not take into account processing time spent on the concurrency control algo itself, which feels like a slight copout since I think this means they ignore lock overhead and use a completely granular locking system (not hierarchical locking), which disfavors optimistic concurrency. This is implementation specific and a lot of additional work to add to the model, and considering there&#39;s some prior work showing that the costs are roughly comparable and negligible compared to access costs, I&#39;m willing to let it&amp;nbsp;go.&lt;/p&gt;
&lt;p&gt;The interesting part comes when they manipulate all the model parameters, and explain how different papers arrived at their different performance results. Basically, under the assumption of infinite resources, optimistic concurrency does splendidly as the level of multiprogramming increases, since locking strategies run into contention and transactions get blocked up. Optimistic transactions still face more conflicts and have to be restarted, but since there the pipeline is always full of running transactions (none are just blocked and using up a queue slot while doing no work), overall throughput continues to increase. Immediate-restart reaches an interesting performance plateau, due to its scheme of trying to match the rate of transaction completion with the rate of re-introducing restarted transactions. This was the model used in a number of prior&amp;nbsp;papers.&lt;/p&gt;
&lt;p&gt;Introducing a very resource limited situation turns things sharply in favor of blocking algos. Blocking performs much better until very high levels of multiprogramming, immediate-restart hits the same plateau for the same reason, and optimistic concurrency performs linearly worse beyond a very small multiprogramming level. Basically, every optimistic conflict detected at the end of a transaction just wasted all of the resources used; immediate restart does better since it will restart if it detects a conflict midway, and also delays restarts to match the completion&amp;nbsp;rate.&lt;/p&gt;
&lt;p&gt;Increasing the number of resources begins to favor optimistic concurrency again, but the price/performance isn&#39;t there since doubling the # of resources does not lead to a doubling in performance. They do a few more different situations, examining different workloads and model assumptions, which you can read yourself if you want to know&amp;nbsp;more.&lt;/p&gt;
&lt;p&gt;Basically, it&#39;s hard to make general statements about performance; things are dependent on your model. It seems that for most real-world use cases though (limited resources, high utilization), blocking is the concurrency control method of choice. It&#39;s also important to carefully control the level of multiprogramming for optimal throughput, since performance tends to peak and then decline as things&amp;nbsp;thrash.&lt;/p&gt;
&lt;p&gt;I also just find it really cool that they explained the performance results of a lot of previous papers within the scope of their own model, basically saying that no one was wrong, just incomplete in their&amp;nbsp;analysis.&lt;/p&gt;

   </content></entry><entry><title>Virtual memory review</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/virtual_memory.html"/><updated>2011-06-11T18:33:00Z</updated><published>2011-06-11T18:33:00Z</published><id>http://www.umbrant.com/blog/2011/virtual_memory.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I&#39;m taking the OS prelim this fall, which means I have to read ~100 papers this summer for background material. Since repetition aids retention, I&#39;m putting notes for papers I read up on my blog. The topics are wide-ranging, so I&#39;m trying to start with the fundamentals and then move on up to the whole-system&amp;nbsp;papers.&lt;/p&gt;
&lt;p&gt;I&#39;m kicking it off with &quot;Virtual Memory, Processes, and Sharing in MULTICS&quot; by Daley and Dennis (1968) and &quot;The Multics Virtual Memory: Concepts and Design&quot; by Bensoussan, Clingen, and Daley (1972). Learn about the joys of segmentation and dynamic linking from classic papers from the 70s! These are slightly infamous papers for some systems students here at Berkeley, due to a certain past OS prelim examiner grilling them on exactly these&amp;nbsp;details.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&amp;nbsp;Review&lt;/h3&gt;
&lt;p&gt;Some people consider &lt;a href=&quot;http://en.wikipedia.org/wiki/Memory_segmentation&quot;&gt;segmentation&lt;/a&gt; to be the most natural way of structuring a program. Most programs are basically a collection of libraries (something really true in modern software engineering). In a segmented virtual memory system, each distinct library is placed in its own separate segment such that it has its own address space. They&#39;re still all mapped unto the same flat physical memory address space, but through per-segment base and offset&amp;nbsp;addresses.&lt;/p&gt;
&lt;p&gt;This isn&#39;t actually used much in modern operating systems for reasons I&#39;m not entirely aware of (I&#39;d guess simplicity and performance), but it&#39;s a pleasingly abstract and indirect way of organizing a program (a hallmark of&amp;nbsp;Multics).&lt;/p&gt;
&lt;h3&gt;Virtual Memory, Processes, and Sharing in&amp;nbsp;MULTICS&lt;/h3&gt;
&lt;p&gt;Multics structured its programs in terms of segments, which could be read/write/execute protected. Segmentation is great for doing memory protection (and something that only recently reemerged as the &lt;a href=&quot;http://en.wikipedia.org/wiki/NX_bit&quot;&gt;NX bit&lt;/a&gt; for flat memory models), since it&#39;s easy to do a compare on any &lt;code&gt;(base+offset)&lt;/code&gt; calculation and see if it falls into a protected range. Segments can still of course be paged, and segmentation and paging are complementary: segmentation for protection, and paging for working set&amp;nbsp;management.&lt;/p&gt;
&lt;p&gt;Addressing in Multics is done in terms of a &lt;em&gt;generalized address&lt;/em&gt;: &lt;code&gt;(segment num + word num)&lt;/code&gt;. The segment number of the currently executing segment is stored in the &lt;em&gt;procedure base register&lt;/em&gt;, so most instructions just need to specify a word number. Indirect addressing (i.e. referencing an address stored at an address) is done with a pair of instructions to have enough bits: one for the segment number, one for the word&amp;nbsp;number.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;descriptor table&lt;/em&gt; is kept of all the segments in a program to map them to hardware addresses. This table maps seg nums to a physical address, and then adds the word num as the offset. This is similar to a page table: virtual to physical address translation. A pointer to the descriptor table is saved as part of the context information of the&amp;nbsp;process.&lt;/p&gt;
&lt;p&gt;Now for the complicated bits: dynamic linking. Clearly, we don&#39;t want each program to have to have its own copy of shared segments (say, libc), and we want some abstraction so we aren&#39;t hardcoding word numbers into our program. This also needs to work for segments linking to other segments which link to other segments, etc., so it gets a little hairy. We also want this to be reasonably fast, e.g. don&#39;t do multiple memory accesses for every dynamically linked call, at least after the first time. In list&amp;nbsp;form:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dynamically linked accesses are specially marked in the program&amp;nbsp;text&lt;/li&gt;
&lt;li&gt;Dynamically linked segments are present at a well-know &lt;em&gt;path name&lt;/em&gt;, e.g. in Linux &lt;code&gt;/usr/lib/ld-linux.so.2&lt;/code&gt;, that the system can search for and find (see&amp;nbsp;LD_LIBRARY_PATH).&lt;/li&gt;
&lt;li&gt;Each segments presents a &lt;em&gt;symbol table&lt;/em&gt;, which defines the call-in points for the segment (static vars, functions,&amp;nbsp;etc)&lt;/li&gt;
&lt;li&gt;Initially, all calls to an external function are to an indirect reference stored as &lt;em&gt;link data&lt;/em&gt; in a per-segment &lt;em&gt;linkage table&lt;/em&gt;. This table is initially set to trap to an OS lookup&amp;nbsp;function. &lt;/li&gt;
&lt;li&gt;A &lt;em&gt;linkage pointer&lt;/em&gt; to the &lt;em&gt;linkage table&lt;/em&gt; is maintained to switch around the table as context changes to different&amp;nbsp;segments.&lt;/li&gt;
&lt;li&gt;On the first reference, the OS lookup function finds the file of the external segment, examines its symbol table, and then &lt;em&gt;links&lt;/em&gt; the two segments by updating the link data in the linkage table. Future references use that indirect address to go straight to the external&amp;nbsp;segment.&lt;/li&gt;
&lt;li&gt;A further complication enters when switching the linkage pointers between linked segments. To determine the new value for the linkage pointer, the calling procedure actually calls into the new segment&#39;s linkage table, which has special instructions to fixup the linkage pointer and then call the called procedure.&lt;ul&gt;
&lt;li&gt;Thus: Caller -&amp;gt; Caller&#39;s linkage table ~&amp;gt; Callee&#39;s linkage table ~&amp;gt;&amp;nbsp;Callee&lt;/li&gt;
&lt;li&gt;This is direct -&amp;gt; indirect -&amp;gt; indirect, plus a fixup, seems&amp;nbsp;expensive...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This really isn&#39;t that different from how Linux does it, the basic idea of &quot;keep a well-known table that fixes itself on the first reference&quot; is a winner. I feel like there are a lot of memory accesses required to traverse all these layers of indirection, since you are calling through multiple layers of indirect addressing, each of which is a pair of&amp;nbsp;instructions.&lt;/p&gt;
&lt;h3&gt;The Multics Virtual Memory: Concepts and&amp;nbsp;Design&lt;/h3&gt;
&lt;p&gt;It&#39;s weird to hear that back in the days of yore, files could not be easily loaded as program text, and the idea of virtual memory for protection, abstraction, and programmer benefit was a new idea. Users were just allocated a range of memory, or &lt;em&gt;core image&lt;/em&gt;, with no sharing between users; if you wanted to work on a &quot;shared file&quot;, that meant doing I/O to copy it into your range, and then another I/O to put it back into the filesystem. Each user&#39;s core image was also an unstructured jumble of instructions and data, which makes system-level sharing and memory protection basically&amp;nbsp;impossible.&lt;/p&gt;
&lt;p&gt;These two goals motivated the design of virtual memory in Multics: sharing and protection. This led to the &lt;em&gt;segmentation&lt;/em&gt;, where each segment appears as a flat, linear namespace to the user program, with read/write/execute/append access rights attached as metadata to the&amp;nbsp;segment.&lt;/p&gt;
&lt;p&gt;Segments are also paged to ease the allocation problem and to support large segments. &lt;em&gt;Descriptor segments&lt;/em&gt; (aka descriptor tables) are also paged for good reason, meaning 4 memory lookups to access a memory location, going through two page tables (one for the descriptor, one for the segment). TLBs work here, but it still sounds slow. Page tables are also a static size, not a&amp;nbsp;tree.&lt;/p&gt;
&lt;p&gt;This paper is a decent overview of Multics, probably would have made sense to read it before the dynamic linking&amp;nbsp;one.&lt;/p&gt;

   </content></entry><entry><title>Android: the Good, the Bad, and the Ugly</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/android_the_good_and_bad.html"/><updated>2011-05-15T23:54:00Z</updated><published>2011-05-15T23:54:00Z</published><id>http://www.umbrant.com/blog/2011/android_the_good_and_bad.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Over the last week, I&#39;ve been doing a crash course in Android programming. &lt;a href=&quot;http://www.eecs.berkeley.edu/~rxin/&quot;&gt;Reynold&lt;/a&gt; and I have been working on our combination CS294-35 Mobile Development project and AMP Lab retreat demo, which is next week. Coming from a traditional web and application development background, there are some things Android does comparatively well, some comparatively poorly, and some that just irritate&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;This isn&#39;t a tutorial, but it&#39;d probably be a useful read if you&#39;re thinking of getting started with Android development. My pain, your&amp;nbsp;gain.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;The&amp;nbsp;Good&lt;/h3&gt;
&lt;p&gt;I like the relatively smooth integration of the Android SDK with Eclipse. It&#39;s pretty easy getting to the &lt;a href=&quot;http://developer.android.com/guide/tutorials/hello-world.html&quot;&gt;Hello World&lt;/a&gt; stage with the Android emulator. Autocompletion works as expected. Debugging is easy, since the emulator will throw stacktraces in LogCat even when not in debug mode, and it&#39;s just as easy to do these things on actual phone hardware. It was also easy to get things running on a real phone; the Motorola Droid I was using didn&#39;t require Verizon activation or rooting or paying for a developers license. Check a few boxes in the settings menu, and you&#39;re off to the&amp;nbsp;races.&lt;/p&gt;
&lt;p&gt;There&#39;s also a thriving community of Android developers. It&#39;s very easy to Google your problems (and believe me, that&#39;s important). StackOverflow seems very&amp;nbsp;helpful.&lt;/p&gt;
&lt;p&gt;I also appreciate having an emulator that works so effectively. I wish that the emulator&#39;s camera had a more useful test pattern, but that&#39;s forgivable. Otherwise, I had very few situations where the emulator behavior differed from actual&amp;nbsp;hardware.&lt;/p&gt;
&lt;h3&gt;The&amp;nbsp;Bad&lt;/h3&gt;
&lt;p&gt;There&#39;s a pretty large jump between Hello World and the next, less trivial tutorial in the series (&lt;a href=&quot;http://developer.android.com/guide/tutorials/notepad/index.html&quot;&gt;Notepad&lt;/a&gt;), and there&#39;s a huge jump from Notepad to making your own&amp;nbsp;app.&lt;/p&gt;
&lt;p&gt;Notepad introduces what has to be the messiest part of Android (and indeed, mobile) development: the &lt;a href=&quot;http://developer.android.com/reference/android/app/Activity.html&quot;&gt;application lifecycle&lt;/a&gt;. Take a look at the flowchart in that link. An Activity is basically a single screen of an application. Whene a new activity is switched in, the old one goes through &lt;code&gt;onPause()&lt;/code&gt; and maybe &lt;code&gt;onStop()&lt;/code&gt;. After that, it&#39;s fair game for the Android task killer, which starts discriminately killing off applications if memory is&amp;nbsp;low.&lt;/p&gt;
&lt;p&gt;This is a huge hassle from a traditional app developer standpoint, since it used to be that the OS would save all your application state on a context switch, and restore it when your app is switched back in. Modify some variables, context switch out, context switch back in, and the variables are how you left them. In Android, that&#39;s no longer true. Now, you are forced to serialize all (all!) of your live state out to one of the Android &lt;a href=&quot;http://developer.android.com/guide/topics/data/data-storage.html&quot;&gt;persistent datastores&lt;/a&gt;, which most of the time means using (SQLite)[http://developer.android.com/guide/topics/data/data-storage.html#db]. If you don&#39;t do this, it means that your app works correctly most of the time (since the task killer doesn&#39;t always kick in), but occasionally, bad things will happen: settings get reset, entered form data disappears, just plain&amp;nbsp;bugs.&lt;/p&gt;
&lt;p&gt;This becomes especially terrible when you&#39;re doing any kind of network programming. This introduces a whole mess of concepts that&#39;d require another full blog post (&lt;a href=&quot;http://developer.android.com/reference/android/app/Service.html&quot;&gt;Service&lt;/a&gt;, &lt;a href=&quot;http://developer.android.com/reference/android/content/ContentProvider.html&quot;&gt;ContentProvider&lt;/a&gt;, and &lt;a href=&quot;http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html&quot;&gt;background reading&lt;/a&gt;), but the basic problem is, how do you reliably make a request to a server if your request might die at any time (due to the application&amp;nbsp;lifecycle)?&lt;/p&gt;
&lt;p&gt;You end up having to watch this &lt;a href=&quot;http://www.youtube.com/watch?v=xHXn3Kg2IQE&quot;&gt;Google I/O talk on how to correctly implement REST in Android&lt;/a&gt;, which features this wonderful diagram that Reynold and I&amp;nbsp;implemented:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;ContentProvider for REST&quot; src=&quot;android_the_good_the_bad_and_the_ugly/android_rest_diagram.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Yuck. Wasn&#39;t REST supposed to be the easy&amp;nbsp;way?&lt;/p&gt;
&lt;p&gt;The very naive way of doing network requests is directly in your Activity, which blocks the UI thread (leading to the app freezing) and is clearly bad practice, even to a novice. The slightly-less-naive way is using a Service, in which you have to start a new thread to make the request (or else it will again block the UI thread). This will mostly work, since Services sort of run in the background, but is still prone to erratic bugs because Services are still under the authority of the Android task killer. So, you end up resorting to ContentProvider, and the filling out the 6 boxes in the diagram you see&amp;nbsp;above.&lt;/p&gt;
&lt;p&gt;The same story can be found for getting phone location. The &lt;a href=&quot;http://developer.android.com/guide/topics/location/obtaining-user-location.html&quot;&gt;documentation page&lt;/a&gt; isn&#39;t bad, but it quickly becomes obvious that it&#39;s complicated to do it both correctly and well. Your app has to balance using GPS vs. celltower triangulation based on accuracy and availability, cache old locations to get an initial fast fix, invalidate said cached locations if they&#39;re too old or too inaccurate, and minimizing overall usage of these radios since they&#39;re the biggest battery killers in a phone. It&#39;s a lot of manual heavy lifting to do it right, and it&#39;s easy to do it incorrectly (presenting inaccurate result) and poorly (quickly draining&amp;nbsp;battery).&lt;/p&gt;
&lt;h3&gt;The&amp;nbsp;Ugly&lt;/h3&gt;
&lt;p&gt;These are some random warts in the platform, not fundamental issues, but annoying (and&amp;nbsp;fixable).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are a lot of concepts thrown at you in the tutorials, and Notepad doesn&#39;t go far enough. I&#39;d like to see some more tutorials, and more beginner-friendly explanations of classes like Activity, Service, Intent, and View, and some high-level advice on designing for the antagonistic application&amp;nbsp;lifecycle.&lt;/li&gt;
&lt;li&gt;The development emulator is really slow. It takes a few minutes to start up, and there&#39;s definitely lag while operating it. Comparatively, the iPhone emulator starts almost instantaneously, and feels just as snappy as a real&amp;nbsp;iPhone.&lt;/li&gt;
&lt;li&gt;No tutorial on how to programatically build a UI. I get that XML is the preferred way since you can do it graphically in Eclipse, but that falls short pretty&amp;nbsp;fast.&lt;/li&gt;
&lt;li&gt;The XML layout is decidedly less powerful than CSS+HTML. No templates, no style rules, lots of repeating the same padding, margin, and textsize parameters in each file. It&#39;s a nightmare for maintainability; fortunately mobile UIs are&amp;nbsp;simple.&lt;/li&gt;
&lt;li&gt;There isn&#39;t a provided library of icons. I think this is a no brainer; I want some basic icons for things like &quot;list&quot;, &quot;settings&quot;, &quot;home&quot; that I see used in core Android, but these aren&#39;t available in the&amp;nbsp;SDK.&lt;/li&gt;
&lt;li&gt;I can&#39;t figure out how to do not-fullscreen Google Maps with panning and zoom. iPhone can do it, but somehow all the Android maps I see that support pan+zoom are&amp;nbsp;fullscreen.&lt;/li&gt;
&lt;li&gt;Hardware and software keyboards have different event listeners and behaviors. Software keyboard has an unfortunate habit of staying open even when changing tabs in a&amp;nbsp;TabView.&lt;/li&gt;
&lt;li&gt;I disliked having to write something like 4 serialization/deserialization routines for every object. This was due to having to store all my state as Java objects, in SQLite, in JSON to talk to the server over REST, and also as visible data on screen. An ORM or something would be&amp;nbsp;great.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This experience made me realize why many Android apps suck: it&#39;s hard to do things the right way, and easy to hack it together the wrong way. I don&#39;t think Android is alone on this one, from what I hear, iPhone isn&#39;t much different in terms of application lifecycle. From the OS point of view, it&#39;s great that any app can be killed at any time to save resources. I&#39;m betting this results in huge wins in battery life, performance, and code size. However, it just shifts that burden onto app developers, who aren&#39;t used to doing this kind of thing, and there aren&#39;t libraries or APIs in place to make this as easy as it should&amp;nbsp;be.&lt;/p&gt;
&lt;p&gt;I&#39;m not totally turned off of mobile app development, since I still believe that mobile is essentially the future of computing, but I really think it could be a lot better. Personally, HP&#39;s webOS appeals to me since HTML+CSS+JS is a &lt;em&gt;much&lt;/em&gt; more natural way of writing applications (and that&#39;s not just my bias as a web developer), and more pure Linux-based OSs like MeeGo are certainly easier to program (but then you lose the noted benefits of the &quot;kill anything at anytime&quot; model). I&#39;m still willing to bet on Android, but it still needs a lot of work before it&#39;s a first-class application development&amp;nbsp;environment.&lt;/p&gt;

   </content></entry><entry><title>External sorting of large datasets</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/external_sorting.html"/><updated>2011-04-16T17:24:00Z</updated><published>2011-04-16T17:24:00Z</published><id>http://www.umbrant.com/blog/2011/external_sorting.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a common interview question: how do you sort data that is bigger than memory? &quot;Big data&quot; in the range of tera or petabytes can now almost be considered the norm (think of Google saving every search, click, and ad impression ever), so this manifests in reality as well. This is also a canonical problem in the database world, where it is referred to as an &quot;external&amp;nbsp;sort&quot;.&lt;/p&gt;
&lt;p&gt;Your mind should immediately turn to divide and conquer algorithms, namely merge sort. Write out intermediate merged output to disk, and read it back in lazily for the next round. I decided this would be a fun implementation and optimization exercise to do in C. There will probably be a follow-up post, since there are lots of optimizations I haven&#39;t yet&amp;nbsp;implemented.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Guido van Rossum (the creator of Python) did this a while ago for the rather smaller (and simpler) case of &lt;a href=&quot;http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html&quot;&gt;sorting a million 32-bit integers in 2MB of RAM&lt;/a&gt;. I took the same approach of a merge sort that writes intermediate runs out to files on disk, buffering file I/O to improve performance. However, since I&#39;m targeting file sizes that are actually larger than RAM (e.g. a couple gigabytes), I need to do more complicated&amp;nbsp;things.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://en.wikipedia.org/wiki/Merge_sort&quot;&gt;basic merge sort&lt;/a&gt; you learn in CS 101 recurses down to the base case of runs of just 1 element, which are progressively merged together in pairs in a logarithmic fashion (arriving at the ultimate O(n*log n) time complexity). This is inefficient for large datasets, because the merging rate is too low. If you&#39;re sorting a 1GB file of 32-bit integers, the first round of merging would generate &lt;code&gt;(1GB/sizeof(int)/2) = (2^30/2^2/2) = 2^27&lt;/code&gt; 8-byte files, which is just too many files. This also leads to the second core problem: small disk I/Os are highly inefficient, since they result in expensive disk seeks. Writing a bunch of 8-byte (or even 8-kilobyte) files effectively randomizes your access pattern, and will choke your throughput. To avoid bad seeks, reads and writes need to be done at about the size of the disk&#39;s buffer (about 16MB these&amp;nbsp;days).&lt;/p&gt;
&lt;p&gt;All of my code is also &lt;a href=&quot;https://github.com/umbrant/extsort&quot;&gt;available on github&lt;/a&gt; if you want to follow along, this post is based more-or-less on the &lt;a href=&quot;https://github.com/umbrant/extsort/tree/3ce53516063bff05570736c412eed032b803ea15&quot;&gt;initial commit&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Basic&amp;nbsp;Approach&lt;/h3&gt;
&lt;p&gt;So our goal is to reduce the number of files written by the first merge step, and also write these files in much bigger chunks. This can be accomplished by increasing the quantum for merging, and doing n-way instead of 2-way&amp;nbsp;merging.&lt;/p&gt;
&lt;p&gt;I increased the merge quantum by sorting each page (4KB) of initial input with quicksort. This way, even with just 2-way merging, the first round for our 1GB of integers only generates &lt;code&gt;(1GB/page_size/2) = (2^30/2^12/2)&lt;/code&gt; = 2^18 intermediate files, which is a lot better than 2^27, but still too large (a quarter million files is a&amp;nbsp;lot). &lt;/p&gt;
&lt;p&gt;N-way merging merges more (many more) than two runs together at once, and is basically the same algorithm as 2-way merging. This finally reduces the level of fan out to manageable levels, and means that the size of the output runs is much larger, meaning that disk I/O can be more easily batched into large 16MB chunks. With 64-way merging we finally get down to &lt;code&gt;(2^18/2^6) = 2^12&lt;/code&gt;, or 4096 intermediate files, which is a pleasant&amp;nbsp;number.&lt;/p&gt;
&lt;p&gt;A further necessary improvement is to incrementally pull large runs off disk (required for later merge steps, when the runs are too large to all fit into memory). I do this at the same granularity as my other I/O operations: 16MB. Currently, this decides the degree of fan out as well, since I pack as many 16MB buffers into memory as I&#39;m allowed, and n-way merge across all of them. This could be a problem if oodles and oodles of memory are allocated to the sort (since n gets large), but my computer with 4GB of RAM can only hold 256 runs, which isn&#39;t that&amp;nbsp;many.&lt;/p&gt;
&lt;h3&gt;Miscellaneous&amp;nbsp;notes&lt;/h3&gt;
&lt;p&gt;There are a few other miscellaneous notes. I ran into the per-process fd limit when doing large merges, so files have to be closed and reopened at the correct offset. I also parallelize the initial quicksorting of pages with a simple worker pool, which really helps speed up the first layer of merging. 
My quicksort also reduces recursion depth by bubblesorting for runs smaller than 5, which is okay since bubblesort is efficient on tiny sets (worst case 6 compares, 6 swaps, compare that to insertion sort). This might or might not increase performance, but it&#39;s fun. Finally, even if 256 buffers can fit into memory, one buffer must always be reserved to be an output buffer (meaning you can do at most a 255-way merge). There&#39;s also some &lt;code&gt;O(n)&lt;/code&gt; memory overhead outside of just storing the data buffers, which you need to be aware of if your memory bound is especially&amp;nbsp;tight.&lt;/p&gt;
&lt;h3&gt;Benchmarking&lt;/h3&gt;
&lt;p&gt;Enough discussion, onto the numbers! This is a situation where I feel like building an autotuner, since my envisioned final version will have a number of knobs to tweak (a future project I suppose). Right now, the two knobs I have to play with are the size of the overall buffer, and the size of I/O&amp;nbsp;buffers. &lt;/p&gt;
&lt;p&gt;I took two sets of numbers. The first set was taken on my laptop, which is a Intel Core i7-620M supporting 4 hyperthreads, 4GB of RAM, and a 7200 RPM disk. The second set was taken on my desktop, an AMD Phenom II X4 965 Black Edition supporting 4 hardware threads, 4GB of RAM, and an 60GB OCZ Vertex 2 SSD. The SSD should help for the smaller I/O buffer sizes, but sequential access shouldn&#39;t be too far&amp;nbsp;apart.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Desktop mergesort&quot; src=&quot;external_sorting/extsort_desktop.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Laptop mergesort&quot; src=&quot;external_sorting/extsort_laptop.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I found these numbers pretty interesting. Each line represents a different total memory size. The graphs indicate that increasing the number of I/O buffer pages leads to better performance as expected, but the small total memory sizes end up performing generally better. Furthermore, my laptop performs better than my desktop with the&amp;nbsp;SSD.&lt;/p&gt;
&lt;p&gt;This can be interpreted as follows. First, linking the fan out of the merge to total memory size is a bad idea. The following table helps make this&amp;nbsp;clear.&lt;/p&gt;
&lt;table border=1&gt;
    &lt;tr&gt;
        &lt;th colspan=4&gt;Fan out of n-way merge&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;th colspan=3&gt;Number of I/O buffer pages (4k)&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;Total memory (MB)&lt;/th&gt;
        &lt;td&gt;1024&lt;/td&gt;&lt;td&gt;2048&lt;/td&gt;&lt;td&gt;4096&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;64&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;128&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;256&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;384&lt;/td&gt;&lt;td&gt;95&lt;/td&gt;&lt;td&gt;47&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;512&lt;/td&gt;&lt;td&gt;127&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;By looking at the laptop graph and this table together, we see that high fanout for 512MB is killing performance, since it&#39;s fine when fan out drops down to 31 at 4096 buffer pages. Conversely, the 64MB case suffers the opposite problem at 4096 pages; a fan out of 3 is too low. Since the two fastest completion times were both with a fan out of 7 (64MB with 2048 pages, 128MB with 4096 pages), I&#39;m betting that it&#39;s around here, but this requires further tuning to decide for&amp;nbsp;sure.&lt;/p&gt;
&lt;p&gt;The second finding is that the sort is currently CPU bound. This isn&#39;t what I expected since there&#39;s a lot of disk I/O, but it seems that the I/O batching techniques are effective. Otherwise, the desktop with the SSD should outpace the laptop. Furthermore, since merging is still single-threaded, the i7 laptop actually might have an advantage because of &lt;a href=&quot;http://en.wikipedia.org/wiki/Intel_Turbo_Boost&quot;&gt;Turbo Boost&lt;/a&gt; kicking up single core performance above the Phenom II&amp;nbsp;desktop.&lt;/p&gt;
&lt;p&gt;Also note that for the relatively low fan outs at 64 and 128MB, the desktop with the SSD has very flat performance as the size of the I/O buffer changes. This is the beauty of fast random accesses, and might be exploitable for better performance since you can save on memory usage by shrinking the I/O&amp;nbsp;buffers.&lt;/p&gt;
&lt;h3&gt;Future&amp;nbsp;work&lt;/h3&gt;
&lt;p&gt;Both of the aforementioned performance issues can be solved by parallelizing the merge step by running multiple n-way merges simultaneously. This lowers the fanout while still using all available memory, and will better balance CPU and I/O time. The number of threads and fan-out of the merge can be parameterized separately, adding two more tuning knobs to the existing knobs of total memory usage and size of I/O buffer (autotuner&amp;nbsp;time?).&lt;/p&gt;
&lt;p&gt;Another potential performance improvement is &lt;a href=&quot;http://en.wikipedia.org/wiki/Multiple_buffering&quot;&gt;double buffering&lt;/a&gt;. This is essentially asynchronous I/O; instead of waiting synchronously for an I/O operation to complete, the CPU switches over to a second buffer and continues processing data. This comes at the cost of doubling memory usage (two buffers instead of one), but is probably especially beneficial for the write buffer since it&#39;s so&amp;nbsp;active.&lt;/p&gt;
&lt;p&gt;There are a few more minor performance tweaks I can think of, but no more really fundamental ones. Let me know in the comments if there&#39;s something I&#39;ve&amp;nbsp;missed.&lt;/p&gt;
&lt;p&gt;A natural extension to this is parallel sorting with multiple machines, but I don&#39;t plan on taking this little C codebase that far. Better to do it properly with Hadoop in a lot less&amp;nbsp;code.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;My best case sorts 1GB of 32-bit integers in 127 seconds in 64MB of memory on my laptop, and I think there&#39;s at least a 2x improvement left with bigger memory sizes. I really enjoy this kind of performance analysis and tuning, since it requires thinking about the storage hierarchy, memory management, and parallelism. It&#39;s been a reasonable two-day project, and I could see this being assigned as an undergrad course project. It doesn&#39;t feel altogether too different from research either, just at a much smaller&amp;nbsp;scale.&lt;/p&gt;
&lt;p&gt;Once again, all the code is available &lt;a href=&quot;https://github.com/umbrant/extsort&quot;&gt;at github&lt;/a&gt;.&lt;/p&gt;

   </content></entry><entry><title>Album first impressions pt. 1</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/album_first_impressions_pt_1.html"/><updated>2011-04-08T00:00:00Z</updated><published>2011-04-08T00:00:00Z</published><id>http://www.umbrant.com/blog/2011/album_first_impressions_pt_1.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I&#39;ve gotten some large influxes of music recently, posting my first impressions of eight albums I&#39;ve given a listen or two. It&#39;s a pretty eclectic mix, new and old stuff. Reviews are unordered, and only qualitative ratings. Unexpectedly, Kanye&#39;s newest work is my favorite album of 2010. More to&amp;nbsp;come.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;&lt;strong&gt;James Brown - Love Power Peace (Live at the Olympia, Paris, 1971)&lt;/strong&gt;: I have a soft spot for live albums, and this has to be among the best. I don&#39;t have much exposure to funk (the closest thing being jazz fusion), but it&#39;s easy to fall in love with the layered big-band instrumentation, high energy, and evocative call-and-response&amp;nbsp;segments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LCD Soundsystem - LCD Soundsystem&lt;/strong&gt; and &lt;strong&gt;LCD Soundsystem - Sound of Silver&lt;/strong&gt;: I liked their most recent album &lt;strong&gt;This Is Happening&lt;/strong&gt; quite a bit, so I checked out the rest of their discography. LCD Soundsystem has a pretty unique sound, rough and unvarnished singing/spoken word over some of the dirtiest bass lines and drops I&#39;ve ever heard. It&#39;s easy to see why their work is such an appealing target for remixers and DJs. I&#39;d argue that James Murphy has taken the art of repetition to perfection, surpassing even French house demigods Daft Punk. I like these two albums less than This Is Happening, but they&#39;re still going on my coding/work playlist since they&#39;re quite listenable for long periods of&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Radiohead - King of Limbs&lt;/strong&gt;: Disappointing. I had to come around to &lt;strong&gt;In Rainbows&lt;/strong&gt;, which is now my favorite Radiohead album, but I don&#39;t think I&#39;m going to acquire the taste of King of Limbs any time soon. It feels heavily influenced by dubstep, a genre of electronic music I&#39;m not particularly infatuated with. I&#39;m willing to give King of Limbs a few more listens because of brand loyalty, but it probably won&#39;t make my list of top 5 favorite Radiohead&amp;nbsp;albums.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Regina Spektor - Begin to Hope&lt;/strong&gt;: She reminds me of Feist, but with better clarity and range. I&#39;m slowly adding female vocal music to my collection (see also: Cat Power), and this is a nice find. Where Feist has this airy, carefree feeling that permeates her music, Spektor is more raw, emotional, and pure. She also has some adorable speech impediment that makes &lt;a href=&quot;http://www.youtube.com/watch?v=5zA4oG4FJFY&quot;&gt;&quot;better&quot; sound like &quot;betto&quot;&lt;/a&gt;.&amp;nbsp;Recommended.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Little Boots - Hands&lt;/strong&gt;: I think she has mild internet fame for her videos &lt;a href=&quot;http://www.youtube.com/watch?v=-CWBgm_-Ggs&quot;&gt;playing&lt;/a&gt; a &lt;a href=&quot;http://www.youtube.com/watch?v=N6tLRCDqJ2c&quot;&gt;Tenori-on&lt;/a&gt;, and now she has an album. It&#39;s pop, which is not normally my thing, so it&#39;s a bit hard for me to judge. It&#39;s a little formulaic (expected), and reminds me of Lady Gaga, with a hip hop or alt rock feel. The autotune still grates on my ears, and is a waste of her talent. Probably won&#39;t go on my regular listening rotation, but recommended for the&amp;nbsp;genre.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Robyn - Body Talk&lt;/strong&gt;: Robyn is a Swedish pop/dance artist who turned down the recording contract that eventually went to Britney Spears. Happily, Robyn also makes much better music. I&#39;ve been watching her since happening across &lt;strong&gt;Body Talk Pt II&lt;/strong&gt;; 2010 saw the release of &lt;strong&gt;Body Talk Pt I&lt;/strong&gt; and &lt;strong&gt;Body Talk Pt II&lt;/strong&gt; as well as this most recent album, which pulls the hits from the first two Body Talk albums and adds a few new songs, forming what is effectively a &quot;best of&quot; compilation. Releasing three albums of new material in a year is impressive; even more impressive is the quality of each song on Body Talk. I could see any song off this album being a single. Recommended even if you don&#39;t like pop. Listen to &lt;a href=&quot;http://www.youtube.com/watch?v=fA3j3VTAsTk&quot;&gt;Fembot&lt;/a&gt; and &lt;a href=&quot;http://www.youtube.com/watch?v=ketX6HITIDU&quot;&gt;Indestructible&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kanye West - My Beautiful Dark Twisted Fantasy&lt;/strong&gt;: There&#39;s a reason this album made numerous &quot;best of 2010&quot; lists. &lt;em&gt;It&#39;s the best fucking album of 2010&lt;/em&gt;. I can hardly believe I&#39;m saying this, but I actually like it more than Arcade Fire&#39;s 2010 entry, The Suburbs. I heard MBDTF&#39;s &lt;a href=&quot;http://www.youtube.com/watch?v=S-qKboHKPEA&quot;&gt;opener&lt;/a&gt;, and was already overwhelmed. I could go through and recommend each track on the album for a different reason, but I&#39;ll promote in particular &lt;a href=&quot;http://www.youtube.com/watch?v=Bm5iA4Zupek&quot;&gt;Runaway&lt;/a&gt; and &lt;a href=&quot;http://www.youtube.com/watch?v=2VOVStL0NW0&quot;&gt;Blame Game&lt;/a&gt;. Kanye was always one of the best hip-hop producers in the business, and now he&#39;s got the rapping chops to match. Sick beats abound. I love the super smooth production, every note is polished to a glossy sheen. I&#39;m impressed with the variety in styles. To conclude: I don&#39;t particularly like hip hop, I don&#39;t like Kanye&#39;s earlier work, I don&#39;t like Kanye the person, but &lt;strong&gt;My Beautiful Dark Twisted Fantasy&lt;/strong&gt; is undeniably brilliant. It&#39;s a hip hop album for the&amp;nbsp;ages.&lt;/p&gt;

   </content></entry><entry><title>Static website hosting on Amazon S3</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/static_hosting_on_s3.html"/><updated>2011-04-01T02:49:00Z</updated><published>2011-04-01T02:49:00Z</published><id>http://www.umbrant.com/blog/2011/static_hosting_on_s3.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Werner_Vogels&quot;&gt;Werner Vogels&lt;/a&gt;, Amazon CTO, posted on his &lt;a href=&quot;http://www.allthingsdistributed.com/&quot;&gt;blog&lt;/a&gt; about a month ago on &quot;&lt;a href=&quot;http://www.allthingsdistributed.com/2011/02/website_amazon_s3.html&quot;&gt;New AWS feature: Run your website from Amazon S3&lt;/a&gt;&quot;. S3 now offers the ability to host static HTML pages directly from an S3 bucket, which is a great alternative for small blogs and sites (provided, of course, that you don&#39;t actually need any dynamic content). This has the potential to greatly reduce your hosting costs. A small Dreamhost/Slicehost/Linode costs around $20 a month, and I used to run this site out of an extreme budget VPS (&lt;a href=&quot;http://virpus.com/&quot;&gt;Virpus&lt;/a&gt;) which was only $5 a month, but I expect to be paying only a few cents per month for S3 (current pricing is just &lt;a href=&quot;http://aws.amazon.com/s3/#pricing&quot;&gt;15&amp;cent; per GB-month&lt;/a&gt;). Of course, you also gain best-of-class durability, fault-tolerance, and scalability from hosting out of S3, meaning that your little site should easily survive a&amp;nbsp;slashdotting.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;The difficulty here is that most of the popular blogging engines require a backing database, and do their content generation dynamically server side. That doesn&#39;t fly with S3; since it is, after all, just a Simple Storage Service, content has to be static and pregenerated. I chose to use &lt;a href=&quot;http://ringce.com/hyde&quot;&gt;Hyde&lt;/a&gt;, a Python content generator that turns templates (based on the &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; templating engine) into HTML. Hyde page templates are dynamic, written in &lt;a href=&quot;http://docs.djangoproject.com/en/dev/topics/templates/&quot;&gt;Django&#39;s templating language&lt;/a&gt; which supports variables, control flow, and hierarchal inheritance. Hyde will parse these templates, fill in the dynamic content, and finally generate static HTML pages suitable for uploading to S3. Ruby folks can check out &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; as an&amp;nbsp;alternative.&lt;/p&gt;
&lt;h3&gt;Caveats&lt;/h3&gt;
&lt;p&gt;To be clear, purely static content won&#39;t suffice for many sites out there (like anything with user-generated content). Even a simple blog like is only feasible because there are web services that fill in the gaps in functionality. &lt;a href=&quot;http://disqus.com/&quot;&gt;Disqus&lt;/a&gt; seems to have cornered the market for comments as a service; you just include a little bit of Javascript and it&#39;s good to go. It&#39;s similarly easy to include a &lt;a href=&quot;http://tweet.seaofclouds.com/&quot;&gt;Twitter widget showing your recent tweets&lt;/a&gt; with another little blob of Javascript, and &lt;a href=&quot;http://www.feedburner.com&quot;&gt;Feedburner&lt;/a&gt; and &lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google Analytics&lt;/a&gt; are defacto analytics tools. There&#39;s barely a need these days to scrape, store, and serve content yourself these days, further obviating the need for a real&amp;nbsp;server.&lt;/p&gt;
&lt;p&gt;This is also clearly a more coding heavy approach to blogging and site generation than most people need. With free blog services like &lt;a href=&quot;http://wordpress.com&quot;&gt;Wordpress.com&lt;/a&gt;, &lt;a href=&quot;http://www.blogger.com&quot;&gt;Blogger&lt;/a&gt;, &lt;a href=&quot;http://www.tumblr.com&quot;&gt;Tumblr&lt;/a&gt;, and &lt;a href=&quot;http://www.posterous.com&quot;&gt;Posterous&lt;/a&gt;, blogging has never been easier or more available. &lt;a href=&quot;http://sites.google.com&quot;&gt;Google Sites&lt;/a&gt; is also a great way of throwing up a quick website. I went with S3 and Hyde because I wanted more customization in the look and feel of the site, I like the Django templating system, and I wanted to play with S3 (especially since Amazon offers &lt;a href=&quot;http://aws.amazon.com/free/&quot;&gt;1 year of free AWS credit&lt;/a&gt;). I also feel a bit safer about my data, since it&#39;s backed by &lt;a href=&quot;http://aws.amazon.com/s3/faqs/#How_is_Amazon_S3_designed_to_achieve_99.999999999%_durability&quot;&gt;Amazon&#39;s eleven 9&#39;s of durability&lt;/a&gt; on S3, it&#39;s on my local machine, and &lt;a href=&quot;https://github.com/umbrant/umbrant-blog&quot;&gt;under version control at github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Hyde&lt;/h3&gt;
&lt;p&gt;Hyde is pretty straightforward for anyone with experience writing Django templates, since it&#39;s basically the Django template engine plus some extra magic content and context tags. The &lt;a href=&quot;https://github.com/lakshmivyas/hyde/blob/master/README.markdown&quot;&gt;Hyde README&lt;/a&gt; and &lt;a href=&quot;https://github.com/lakshmivyas/hyde/wiki&quot;&gt;github wiki&lt;/a&gt; are somewhat helpful in laying this out. Essentially, Hyde lets you assign per-page metadata that can be accessed by other pages as they walk the directory structure of your content; your URL structure mirrors your folder structure. By default, this metadata includes a &lt;code&gt;created&lt;/code&gt; field that fuels the magic &lt;code&gt;recents&lt;/code&gt; template tag which gets the most recent content from a directory (like your blog). There are a few more Hyde specific features which you can read about on &lt;a href=&quot;https://github.com/lakshmivyas/hyde/wiki/Templating-Guide&quot;&gt;the wiki page on templating&lt;/a&gt;, and the &lt;a href=&quot;http://docs.djangoproject.com/en/dev/ref/templates/builtins/&quot;&gt;Django templating reference&lt;/a&gt; is also&amp;nbsp;useful.&lt;/p&gt;
&lt;p&gt;I still found myself a little stuck, and what was most useful was reading the source for the skeleton site that Hyde generates for you initially, and the &lt;a href=&quot;https://github.com/sjl/stevelosh/&quot;&gt;code that Steve Losh&lt;/a&gt; uses to generate his own blog. To help you out, I&#39;ve &lt;a href=&quot;https://github.com/umbrant/umbrant-blog&quot;&gt;published the code&lt;/a&gt; for this site on github too. It might be useful to read &lt;a href=&quot;http://stevelosh.com/blog/2010/01/moving-from-django-to-hyde/&quot;&gt;Steve&#39;s write up on moving from Django to Hyde&lt;/a&gt; as&amp;nbsp;well.&lt;/p&gt;
&lt;p&gt;A few nice features of Hyde I like are the ability to automatically compress Javascript and CSS with jsmin and cssmin, and support for writing posts in &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt;, which is a lot easier and more portable than HTML. There&#39;s also support for writing &quot;higher level CSS&quot; (CleverCSS, HSS, LessCSS), but I never understood the point of these and didn&#39;t use&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;The features I had to add to the skeleton code are a draft status for posts, and the &quot;Recent Posts&quot; and &quot;Archive&quot; sections on the sidebar. Drafts were done by adding a metadata &lt;code&gt;draft: True&lt;/code&gt; tag to draft posts, and modifying all my &quot;listing&quot; pages to exclude these posts (like the home page, archive, recent posts, atom feed). The &quot;Recent Posts&quot; and &quot;Archive&quot; sidebar use &lt;code&gt;page.walk&lt;/code&gt; to traverse the blog directory and the &lt;code&gt;recents&lt;/code&gt; tag the most recent posts. These posts are then filtered with if statements to exclude non-draft content. This is all slightly hacky, since if you want to show the 5 most recent blog posts (as returned by &lt;code&gt;recents 5&lt;/code&gt;), you might have less than 5 posts after filtering out drafts. This is worked around by not dating drafts until publication (which gives them a default date in&amp;nbsp;1900).&lt;/p&gt;
&lt;p&gt;I also had to modify Hyde&#39;s &lt;code&gt;page.walk&lt;/code&gt; and &lt;code&gt;page.walk_reverse&lt;/code&gt; to walk directories in lexicographically sorted order, but I&#39;m hoping that&#39;s been fixed in git (I was using version&amp;nbsp;0.4).&lt;/p&gt;
&lt;h3&gt;S3&lt;/h3&gt;
&lt;p&gt;There is &lt;a href=&quot;http://aws.typepad.com/aws/2011/02/host-your-static-website-on-amazon-s3.html&quot;&gt;plenty&lt;/a&gt; of &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?WebsiteHosting.html&quot;&gt;documentation&lt;/a&gt; on &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?WebsiteHosting.html&quot;&gt;how&lt;/a&gt; to set up an S3 bucket as a website. It&#39;s pretty easy, I didn&#39;t have any trouble with&amp;nbsp;this.&lt;/p&gt;
&lt;p&gt;Making your existing domain name point to your S3 bucket is a little more tricky. S3 provides a URL for your bucket (in my case, &lt;a href=&quot;http://www.umbrant.com.s3-website-us-west-1.amazonaws.com/&quot;&gt;http://www.umbrant.com.s3-website-us-west-1.amazonaws.com/&lt;/a&gt;). The first problem is a limitation of DNS: you can&#39;t make your zone apex a CNAME. If that was gibberish, it means that you can&#39;t make your plain domain name (&lt;a href=&quot;http://umbrant.com&quot;&gt;http://umbrant.com&lt;/a&gt;) an alias for another domain name, like your S3 bucket&#39;s. Subdomains don&#39;t have this limitation, which is why you&#39;re viewing this blog at &lt;a href=&quot;http://www.umbrant.com&quot;&gt;http://www.umbrant.com&lt;/a&gt;, happily CNAME&#39;d to my S3 bucket. My zone apex then does a redirect to the &lt;code&gt;www&lt;/code&gt; subdomain; this redirect is a service provided by some registrars, or you can beg a friend with a&amp;nbsp;server.&lt;/p&gt;
&lt;p&gt;I just lied to you a little about how this works. Notice that if you &lt;code&gt;dig www.umbrant.com&lt;/code&gt;, you get the&amp;nbsp;following:&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dig&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;umbrant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;sr&quot;&gt;&amp;lt;snip&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QUESTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SECTION:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;umbrant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;IN&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ANSWER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SECTION:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;umbrant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;831&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IN&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;CNAME&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;website&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amazonaws&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;website&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amazonaws&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;  &lt;span class=&quot;mf&quot;&gt;204.246.162.151&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;sr&quot;&gt;&amp;lt;snip&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;My subdomain isn&#39;t actually CNAME&#39;d to my S3 bucket domain name, I&#39;ve set it to alias directly to &lt;code&gt;s3-website-us-west-1.amazonaws.com&lt;/code&gt;. This is a mild optimization that saves a DNS lookup; if you &lt;code&gt;dig&lt;/code&gt; my bucket domain name, you see that it&#39;s CNAME&#39;d to &lt;code&gt;s3-website-us-west-1.amazonaws.com&lt;/code&gt; anyway, which finally gets turned into the IP address for an S3 server (the A record). This server uses the referring domain name (&lt;code&gt;www.umbrant.com&lt;/code&gt;) to look up the S3 bucket with the same name. This system also means that if someone&#39;s already made a bucket in your region with the same name as your subdomain, you&#39;ve got to choose a different subdomain (thanks to S3&#39;s flat keyspace). In other words, when using S3, your bucket name and subdomain must be the&amp;nbsp;same.&lt;/p&gt;
&lt;p&gt;Uploading files to S3 isn&#39;t too bad. I&#39;m sure there are existing tools out there for interfacing with S3 on the commandline, but I rolled my own in Python with the &lt;a href=&quot;http://pypi.python.org/pypi/simples3/1.0&quot;&gt;SimpleS3&lt;/a&gt; library available on PyPI. It&#39;s basically rsync-for-S3 with some issues; it doesn&#39;t delete old files from S3, the parsing isn&#39;t bulletproof, and it uses modtimes to check for updates instead of checksums (which I plan on implementing soon, right now it&#39;s almost my entire blog each time I re-run Hyde). However, it does work, and it is really simple to&amp;nbsp;use.&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;simples3&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# Config options&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;ACCESS_KEY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;YOUR_ACCESS_KEY&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;SECRET_KEY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;YOUR_SECRET_KEY&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# Change this&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;BUCKET_NAME&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&amp;quot;www.umbrant.com&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# Change this too, make sure to edit your region and bucket name&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;BASE_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;https://s3-us-west-1.amazonaws.com/www.umbrant.com&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# NO TRAILING SLASH&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;SOURCE_DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/home/andrew/dev/umbrant_static/deploy&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;IGNORE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;s&quot;&gt;&amp;quot;\.(.*).swp$&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;~$&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# ignore .swp files&lt;/span&gt;&lt;br /&gt;         &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# code&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;ignore_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IGNORE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;n&quot;&gt;ignore_re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# open bucket&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;S3Bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BUCKET_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;br /&gt;                  &lt;span class=&quot;n&quot;&gt;secret_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SECRET_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BASE_URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;c&quot;&gt;# recursively put in all files in SOURCE_DIR&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SOURCE_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;n&quot;&gt;relroot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SOURCE_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;c&quot;&gt;# root directory files should not have a preceding &amp;quot;/&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;c&quot;&gt;# puts the files in a blank named directory, not what we want&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relroot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relroot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class=&quot;c&quot;&gt;# check in the ignore list&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;ignore&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignore_re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Ignoring&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;n&quot;&gt;ignore&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;modtime&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_mtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class=&quot;c&quot;&gt;# check if it&amp;#39;s changed with modtimes&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;public-read&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Uploading&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;metadata&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;modtime&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; \&lt;br /&gt;        &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;metadata&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;modtime&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_mtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;public-read&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;br /&gt;                       &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Uploading&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Skipping&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;h3&gt;Final&amp;nbsp;remarks&lt;/h3&gt;
&lt;p&gt;This was a pretty reasonable and fun 2 days of effort, most of which was spent on tuning the CSS template and writing content, not wrangling code. Hyde doesn&#39;t feel very mature (documentation is lacking, example skeleton site is slightly broken, the sorting bug), but it works well enough and is good for people transitioning from Django. I&#39;m very positive about S3 and Amazon Web Services in general (&lt;a href=&quot;http://blog.reddit.com/2011/03/why-reddit-was-down-for-6-of-last-24.html&quot;&gt;modulo Elastic Block Store being terrible&lt;/a&gt;, but that&#39;s a rant for another day), since my site is now essentially impervious to failure. It&#39;s also pleasing to see top management like Werner Vogels dogfooding Amazon&#39;s&amp;nbsp;features.&lt;/p&gt;

   </content></entry><entry><title>Hello world!</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2011/hello_world.html"/><updated>2011-03-30T01:34:29Z</updated><published>2011-03-30T01:34:29Z</published><id>http://www.umbrant.com/blog/2011/hello_world.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Hello world! This is my new blog and profile site, purely static HTML hosted out of S3. 
Since I find this to be a neat trick, I&#39;m going to make the howto into my second-ever blog post.
This site is still very much under development, I have lots of ideas for neat features I want to add (comments being the first), but it&#39;s good enough to put&amp;nbsp;live.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>Cake presented at SoCC</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2012/cake.html"/><updated>2012-10-15T14:00:00Z</updated><published>2012-10-15T14:00:00Z</published><id>http://www.umbrant.com/blog/2012/cake.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I just presented some of our work at Berkeley at SoCC, on &quot;Cake: Enabling High-Level SLOs on Shared Storage.&quot;
It&#39;s a coordinated, multi-resource scheduler for storage workloads, which enables consolidation of front-end and backend workloads while meeting high-level performance requirements of the front-end workload. 
Consolidation has advantages in terms of economic costs (reducing overprovisioning and underutilization), and also significantly reducing the latency of traditional unconsolidated copy-then-process analytics&amp;nbsp;cycles.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;/papers/socc12-cake.pdf&quot;&gt;PDF of the paper&lt;/a&gt; and the &lt;a href=&quot;/papers/socc12-cake-slides-awang.pptx&quot;&gt;slides from my presentation&lt;/a&gt; are available on my &lt;a href=&quot;/research.html&quot;&gt;research page&lt;/a&gt;.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>MinuteSort with Flat Datacenter Storage</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2012/flat_datacenter_storage.html"/><updated>2012-07-02T22:42:00Z</updated><published>2012-07-02T22:42:00Z</published><id>http://www.umbrant.com/blog/2012/flat_datacenter_storage.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Microsoft Research recently crushed the &lt;a href=&quot;http://sortbenchmark.org/&quot;&gt;world record for MinuteSort&lt;/a&gt;, sorting 1.4TB in a minute. This replaces the former record held by Yahoo&#39;s 1406 node Hadoop cluster in the Daytona MinuteSort category, and means that Hadoop no longer holds any world sorting record&amp;nbsp;titles.&lt;/p&gt;
&lt;p&gt;I found MSR&#39;s approach of &quot;&lt;a href=&quot;http://sortbenchmark.org/FlatDatacenterStorage2012.pdf&quot;&gt;MinuteSort with Flat Datacenter Storage&lt;/a&gt;&quot; (FDS) to be intriguing. Most of the prior sort winners (e.g. Hadoop, TritonSort) try to colocate computation and data, since you normally pay a throughput (and thus latency) cost to go over the network. FDS separates out compute from storage, heavily provisioning a full bisection bandwidth network to match the I/O rate of the hard disks on storage&amp;nbsp;nodes.&lt;/p&gt;
&lt;p&gt;I&#39;m going to give a rundown of the paper, and then pull out salient points for Hadoop at the&amp;nbsp;end.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Storage&lt;/h3&gt;
&lt;p&gt;FDS the storage system is pretty simple. It&#39;s a straight-up blob store, where &lt;em&gt;blobs&lt;/em&gt; are arbitrarily large, but are read and written in 8MB chunks called &lt;em&gt;tracts&lt;/em&gt;. Blobs are identified with a 128-bit GUID, and tracts are 8MB offsets into the blob. Choosing 8MB means that the disks get essentially full sequential write bandwidth, since seek times are amortized over these long reads. It provides asynchronous APIs for read and write operations; pretty necessary for doing high-performance parallel&amp;nbsp;I/O.&lt;/p&gt;
&lt;p&gt;Data is placed on storage nodes (aka &lt;em&gt;tractservers&lt;/em&gt;) through a simple hashing scheme. There&#39;s no Namenode like in Hadoop. Instead, clients cache a &lt;em&gt;tract locator table&lt;/em&gt; (TLT) which lists all of the tractservers in immutable order. Lookups for a tract in a blob are done by hashing the blob GUID, adding the tract number, and then moduloing to index into the TLT. The paper doesn&#39;t talk about replication for availability and durability, but it&#39;d be easy to tack it on the way MongoDB does it, which is making identical clones of each tractserver and then doing failover with Paxos or some&amp;nbsp;such.&lt;/p&gt;
&lt;p&gt;There&#39;s some more complexity in the hashing scheme to handle load balancing, which is reminiscent of Chord&#39;s &lt;em&gt;virtual node&lt;/em&gt; approach. Client access through the TLT is normally going to be sequential, since clients will mostly be sequentially scanning through the tracts of a blob. Remember that to find the TLT index, the blob ID is hashed, but the tract number is just added. This means you can get &lt;em&gt;convoy effects&lt;/em&gt; where clients auto-synchronize as they wait on a slow tractserver, and from then on move in lockstep through the list. This is countered by taking 20 copies of the list of tractservers, shuffling each copy, and then concatenating them together. This super-list prevents auto-synchronizing lockstep, and also does a good job of balancing data distribution across&amp;nbsp;tractservers.&lt;/p&gt;
&lt;p&gt;An interesting note is that storage nodes are heterogeneous, with the authors saying they &quot;bought, begged, and borrowed&quot; a mix of machines. They again borrow from Chord&#39;s virtual nodes by adjusting the number of times a node appears in the TLT proportionally to its I/O capability. This assumes that I/O rate and amount of storage on a node are also proportional, since there just this one mechanism of tweaking the # of appearances in the TLT to distribute both I/O and data. This assumption doesn&#39;t hold up since hard disk sizes are increasing faster than I/O rates, but maybe they didn&#39;t have heterogeneous enough hardware to care. In any case, it&#39;s not addressed in the&amp;nbsp;paper.&lt;/p&gt;
&lt;h3&gt;Network&lt;/h3&gt;
&lt;p&gt;Each node has dual 10GigE Ethernet, connected by a full-bisection bandwidth Clos network. Since their flows are all short and bursty (random 8MB accesses), TCP&#39;s slow start is a bad fit. Instead, they use a windowed RTS/CTS scheme which limits the number of flows at each receiver, and thus limits congestion in the Clos. Control messages are sent through separate TCP connections to improve latency; this is because TCP&#39;s per-flow fairness allows them to skip&amp;nbsp;buffering.&lt;/p&gt;
&lt;p&gt;They also try to zero-copy everything right from disk to network to application buffers. This is basically a requirement if you&#39;re trying to beat a sorting&amp;nbsp;record.&lt;/p&gt;
&lt;h3&gt;Phases&lt;/h3&gt;
&lt;p&gt;There are two phases: a read and partition phase, and a write phase. During the &lt;em&gt;read phase&lt;/em&gt;, compute nodes read tracts of unsorted data from storage nodes, sort the data into buffers based on partition, and then stream these buffers to the node responsible for the partition. After the data is partitioned, each node does a &lt;em&gt;write phase&lt;/em&gt; where it sorts its partition and writes it out to disk. Since they&#39;re doing MinuteSort, the dataset fits entirely in memory and only has to be read and written&amp;nbsp;once.&lt;/p&gt;
&lt;p&gt;Separated storage makes handling stragglers very easy. Since the initial read phase is lovingly stateless, any node can read and partition any tract of data. This means a dynamic work queue can be used for load balancing, which is an approach I really like. Whenever a node finishes a work item, it polls a central coordinator for more work to do. As long as the size of each work item isn&#39;t too big, stragglers no longer have a major impact on overall job completion&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;To determine the right size for work items, the authors use a clever dynamic &quot;Zeno allocation&quot; scheme. Basically, work items decrease in size as the job nears completion. This makes sense because stragglers hurt you at the end of the job, so it&#39;s worth the overhead of using smaller work items for that last wave of&amp;nbsp;processing. &lt;/p&gt;
&lt;p&gt;Computation and communication are overlapped during the read phase. As a node receives buffers of partitioned data, it saves up until it has a 250MB chunk and then quicksorts it in the background. Thus during the write phase, each node has to do maybe one more 250MB quicksort, then just merges all of its buffers together as they&#39;re written out to disk. This hierarchy of sorts is pretty much the same approach I used in my &lt;a href=&quot;/blog/2014/bucket_list_fish.html&quot;&gt;external sorting implementation&lt;/a&gt;. It also lets them do external sorting of datasets bigger than&amp;nbsp;memory.&lt;/p&gt;
&lt;h3&gt;Hadoop&amp;nbsp;comparison&lt;/h3&gt;
&lt;p&gt;There are a number of reasons why this is significantly faster than Hadoop&amp;nbsp;MapReduce.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In FDS, data is only read and written to disk once. Hadoop&#39;s intermediate data is sorted and spilled to disk by the mapper, while FDS keeps this entirely in&amp;nbsp;memory.&lt;/li&gt;
&lt;li&gt;FDS also streams and sorts data during the read phase, while Hadoop has a barrier between its map and reduce phases. Hadoop can do some pre-aggregation in the mapper with a combiner, but not as flexibly as in FDS, not on the reduce side, and not without hogging an entire task&amp;nbsp;slot.&lt;/li&gt;
&lt;li&gt;FDS has better task scheduling, with a dynamic work queue and dynamic work item&amp;nbsp;sizes.&lt;/li&gt;
&lt;li&gt;FDS uses a single process for both the read and write phases, so the JVM startup cost is gone, and there&#39;s no unnecessary movement of data between address&amp;nbsp;spaces.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, FDS gets away with some of this because of its problem domain. The MapReduce paradigm is optimized for simplicity and scale-out rather than raw performance. Serializing intermediate data and having non-overlapping map and reduce phases makes fault-tolerance easier. If a reducer dies, a new reducer can just read all the intermediate data off disk. If a mapper dies, there&#39;s no icky problems with a reducer having already processed some of the mapper&#39;s output. Hadoop is also optimized for overall cluster throughput rather than completion time of this single MinuteSort job, which is part of why it uses much larger splits (64MB or 128MB) and doesn&#39;t have dynamic task sizing (hurts overall&amp;nbsp;throughput).&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;What I get out of this is that MSR built a highly-optimized blob store, connected it to a 20Gbit Clos network, and ran an in-memory sort. While it does crush Hadoop, I don&#39;t think there&#39;s much here over TritonSort, since their 1.47TB/min for the Indy MinuteSort wasn&#39;t that much better than TritonSort&#39;s 1.35TB/min in 2011, and TritonSort was built with academic dollars rather than MSR dollars. I like the idea of separated storage, but building out a full Clos network is expensive. I liked their tricks of hashing to determine tract locations, but they really should present a full fault-tolerance and durability story. Network scheduling in the general case is also more complicated than in FDS sort, and this is part of why Clos aren&#39;t so useful in practice: they&#39;re nice to program for, but are hard to keep fully&amp;nbsp;utilized.&lt;/p&gt;
&lt;p&gt;I think the main areas where Hadoop could see improvement are increased use of zero-copy I/O and a better story for intermediate data and use of memory in the cluster. Since zero-copy has improved greatly in recent releases of Hadoop, the big remaining issue is memory caching. I need to toot the Berkeley horn here and mention &lt;a href=&quot;http://www.spark-project.org/&quot;&gt;Spark&lt;/a&gt; and &lt;a href=&quot;https://www.usenix.org/system/files/conference/nsdi12/pacman.pdf&quot;&gt;PACMan&lt;/a&gt;, which both address this problem. We really need the equivalent of HDFS for memory, since it can have huge performance benefits when there&#39;s iterative computation or hot data. The difficulty here is settling on the right abstractions and&amp;nbsp;mechanisms.&lt;/p&gt;

   </content></entry><entry><title>JVM Performance Tuning (notes)</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2012/twitter_jvm_tuning.html"/><updated>2012-01-18T00:15:00Z</updated><published>2012-01-18T00:15:00Z</published><id>http://www.umbrant.com/blog/2012/twitter_jvm_tuning.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;A presentation by Attila Szegedi titled &lt;a href=&quot;http://www.infoq.com/presentations/JVM-Performance-Tuning-twitter&quot;&gt;&quot;Everything I Ever Learned about JVM Performance Tuning @twitter&quot;&lt;/a&gt; has been floating around for a few months. I&#39;ve restructured much of the content into a set of notes. This covers the basics of memory allocation and garbage collection in Java, the different garbage collectors available in HotSpot and how they can be tuned, and finally some anecdotes from Attila&#39;s experiences at&amp;nbsp;Twitter.&lt;/p&gt;
&lt;p&gt;I&#39;m still fuzzy on some things, so it&#39;s not ground truth. If more experienced people weigh in, I&#39;ll fix things up. The very informative hour-long presentation is still highly&amp;nbsp;recommended.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;The Price of an&amp;nbsp;Object&lt;/h3&gt;
&lt;p&gt;Java, as an object-oriented language, naturally results in the creation of a lot of objects. This is one of the things you give up as opposed to a language like C; even basic data-wrapper objects are much heavier weight than structs. The minimal object is &lt;strong&gt;24 bytes&lt;/strong&gt;: 16B of object overhead, and 8B for the pointer to that object. This includes arrays, which also incur 24B for an empty array, 4B for the size of the array, then per-item costs after that (you only need one&amp;nbsp;pointer).&lt;/p&gt;
&lt;p&gt;Primitive types don&#39;t need require the 16B of object overhead, and are thus can be a much more compact representation. However, beware &lt;em&gt;autoboxing&lt;/em&gt;: many provided data structures will automatically convert your nice compact &lt;code&gt;int&lt;/code&gt; into a big fat &lt;code&gt;Integer&lt;/code&gt;. Using plain old arrays of primitive types can be the best choice. It&#39;s definitely better than allocating lots of tiny objects which will end up being substantially&amp;nbsp;overhead.&lt;/p&gt;
&lt;p&gt;As a side note not mentioned by Attila, also beware the use of the Java &lt;code&gt;String&lt;/code&gt; class, since it can &lt;strong&gt;double&lt;/strong&gt; your in-memory storage costs. Java internally uses UTF-16 for its strings, which is a 2-byte character encoding. Compare this to the more common UTF-8 or ASCII encodings, which are both 1-byte. This glosses over the details (UTF-16 and UTF-8 are variable-length encodings, and can be up to 4 bytes per character), but this doubling holds true for the common&amp;nbsp;case.&lt;/p&gt;
&lt;p&gt;There are two more twists here. First, Java pads out all objects to the nearest 8-byte boundary, which fattens up objects a bit more. This isn&#39;t all bad though, since it aids the second twist: pointer compression. Beneath 32GB of heap, the JVM will actually only use 4B per pointer instead of 8B. Why is 4B enough for 32GB? Since everything is padded out to 8B, the pointer can just be left shifted by three bits before doing the normal byte addressing. However, this means if you want a heap bigger than 32GB, you need to jump up a lot; Attila says&amp;nbsp;48GB.&lt;/p&gt;
&lt;h3&gt;JVM Memory&amp;nbsp;Management&lt;/h3&gt;
&lt;p&gt;The JVM heap is split up into two &lt;em&gt;generations&lt;/em&gt; which are garbage collected at different rates. All objects are allocated in the &lt;em&gt;young generation&lt;/em&gt;; more specifically, they are allocated within &lt;em&gt;Eden&lt;/em&gt; in the young generation. As objects survive GC rounds, they get copied to the two successive &lt;em&gt;survivor&lt;/em&gt; regions within the young generation, before ultimately being &lt;em&gt;tenured&lt;/em&gt; to the &lt;em&gt;old generation&lt;/em&gt;, which gets GC&#39;d less frequently than the young&amp;nbsp;generation.&lt;/p&gt;
&lt;p&gt;This means that Java optimizes for the common case of short-lived trash that can get quickly collected in the young generation. Eden allocation and garbage collection is also really cheap. It&#39;s treated kind of like stack allocation; creating a new object usually just requires bumping the pointer that defines the end of Eden. Garbage collection is also simple; live objects get copied out to the first survivor, and then the Eden pointer gets zeroed back to the start of the heap. Trash doesn&#39;t need to be explicitly zeroed out, so deallocation is &quot;free&quot;. Note that this efficiency is true only for small objects; larger objects (megabytes) are allocated in a different and more expensive way directly to the old&amp;nbsp;generation.&lt;/p&gt;
&lt;p&gt;Garbage collection happens when Eden fills up. This might sound bad since young GCs are stop-the-world and happen erratically, but young generation GC time is proportional to the number of live young objects, which is usually small compared to the amount of trash. Concurrent GC can happen in the background, avoiding that nasty stop-the-world pause, but only for the old generation, and it&#39;s not perfect. More on this&amp;nbsp;later.&lt;/p&gt;
&lt;h3&gt;Garbage Collector&amp;nbsp;Tuning&lt;/h3&gt;
&lt;p&gt;Attila iterates multiple times that the more memory you can give the young generation, the better, since allocation and deallocation is so cheap. I think he then backpedaled a bit though, because really big young generations can lead to long pauses while the live objects are copied around. What you want is a young generation big enough to hold active and tenuring objects, and for long-lived objects to quickly tenure and reach the old generation. However, you also don&#39;t want survivors to get forced to the old generation early by memory pressure on the young&amp;nbsp;generation.&lt;/p&gt;
&lt;p&gt;With this in mind, lets talk about the different garbage collectors available in HotSpot. They can be divided into two categories, throughput and&amp;nbsp;latency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Throughput: scheduled to run when the JVM runs out of memory. Stop-the-world operation.&lt;ul&gt;
&lt;li&gt;SerialGC: Single-threaded garbage collector. Sun probably wrote this one first, and it&#39;s around for legacy&amp;nbsp;reasons.&lt;/li&gt;
&lt;li&gt;ParallelGC and ParallelOldGC: Multi-threaded garbage collectors. ParallelOldGC is actually better than ParallelGC since it cleans both the young and old generations (rather than just the young&amp;nbsp;generation).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Latency: scheduled to run periodically by the JVM when it has spare cycles. Can still result in stop-the-world if the GC can&#39;t clean fast enough and memory runs out.&lt;ul&gt;
&lt;li&gt;ConcMarkSweepGC (CMS): Concurrent and tries to be &quot;low pause&quot;. CMS has a number of caveats though. It kicks in when allocated memory passes a threshold, meaning you need to overprovision memory by 25-33% to give it a buffer to allocate with while it cleans. It also doesn&#39;t compact memory, so you can get fragmentation that leads to stop-the-world pauses. As stated earlier, it also only cleans the old generation, and uses a throughput collector for the young&amp;nbsp;generation.&lt;/li&gt;
&lt;li&gt;G1GC: undocumented black magic that Attila had no experience with, and explicitly didn&#39;t not cover in his presentation. I found &lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/tech/g1-intro-jsp-135488.html&quot;&gt;this link to the Oracle documentation&lt;/a&gt; though, it&#39;s supposed to be a better replacement for&amp;nbsp;CMS.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a lot of options here, so Attila breaks it down into some simple&amp;nbsp;heuristics.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look for ways to reduce the application&#39;s memory consumption. Less memory pressure means less garbage&amp;nbsp;collection.&lt;/li&gt;
&lt;li&gt;Try a throughput collector with adaptive sizing turned on, which lets the JVM figure out the best sizes for the different generations. If this works, great! &lt;code&gt;-XX:+PrintHeapAtGC&lt;/code&gt; can be helpful&amp;nbsp;here.&lt;/li&gt;
&lt;li&gt;Next, try ConcMarkSweepGC. &lt;code&gt;-verbosegc&lt;/code&gt; and &lt;code&gt;-XX:+PrintGcDetails&lt;/code&gt; are useful here. This is a situation where you might adjust the young generation to reduce the pause from young gen GC time, but also make sure that the survivor regions aren&#39;t filling&amp;nbsp;up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you&#39;re interested, a quick search turned up two links to official Oracle documentation for the HotSpot 1.4.2 garbage collectors. It&#39;s a bit dated, but I think most of it still&amp;nbsp;applies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://java.sun.com/docs/hotspot/gc1.4.2/&quot;&gt;Tuning Garbage Collection with the 1.4.2 Java Virtual&amp;nbsp;Machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://java.sun.com/docs/hotspot/gc1.4.2/faq.html&quot;&gt;FAQ about Garbage Collection in the Hotspot Java Virtual&amp;nbsp;Machine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Programming&amp;nbsp;Anecdotes&lt;/h3&gt;
&lt;p&gt;These are miscellaneous tips pulled from the talk. Most of them boil down to using less memory, which is a much better solution than trying to tune the GC. There were some out-of-the-box solutions, and some Twitter-specific things&amp;nbsp;too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get used to profiling your code, especially third party libraries.&lt;ul&gt;
&lt;li&gt;For instance, Guava sucks up 2KB each time you make a map by default to handle concurrency&amp;nbsp;cases.&lt;/li&gt;
&lt;li&gt;Don&#39;t use Thrift&#39;s RPC classes as your domain objects, deserialize rather than keeping them around. They have extra overhead compared to normal&amp;nbsp;objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Normalize your data when possible. If some data is shared between multiple objects, have them all point to the same instance instead of each having their own&amp;nbsp;copy.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Beware of worker thread pools sharing connections to many storage servers; this can result in &lt;code&gt;m * n&lt;/code&gt; cached connection objects if you&#39;re using thread&amp;nbsp;locals.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s better to instead use fewer threads and asynchronous&amp;nbsp;I/O.&lt;/li&gt;
&lt;li&gt;Also, don&#39;t be afraid of just creating a new object on demand. It&#39;s cheap in Eden, just a single pointer&amp;nbsp;bump!&lt;/li&gt;
&lt;li&gt;Synchronized objects are another alternative to thread&amp;nbsp;locals.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you&#39;re having trouble packing it all into one JVM, try using&amp;nbsp;multiple!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Twitter had a service that would have a terrible GC pause every three days. Solution: just bounce the machine after less than three&amp;nbsp;days.&lt;/li&gt;
&lt;li&gt;Don&#39;t write your own memory manager, and stop if you find yourself doing something ugly with byte buffers.&lt;ul&gt;
&lt;li&gt;The one notable exception is if you need it for a very limited and simple case. Cassandra has its own slab allocator with fixed size slabs that are flushed to disk when memory is full. This is so simple that it&#39;s&amp;nbsp;okay.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Oracle told Twitter that the garbage collector isn&#39;t actually complained about that much, since people have figured out how to engineer around&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;You should never have to call the garbage collector yourself in&amp;nbsp;code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This is far from a complete discussion on memory management in Java, but it&#39;s got some easy and immediately applicable findings, and I hope these notes help direct further reading. I want to give Bill Pugh&#39;s super detailed &lt;a href=&quot;http://www.cs.umd.edu/~pugh/java/memoryModel/&quot;&gt;&quot;The Java Memory Model&quot;&lt;/a&gt; a read, and there are a few Java performance tuning books on my Amazon wishlist&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Again, leave a comment if something is unclear or incorrect, and I&#39;ll do my best to fix it&amp;nbsp;up.&lt;/p&gt;

   </content></entry><entry><title>Year in review: 2011 (personal)</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2012/year_in_review_2011_personal.html"/><updated>2012-01-05T21:44:00Z</updated><published>2012-01-05T21:44:00Z</published><id>http://www.umbrant.com/blog/2012/year_in_review_2011_personal.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I like to take some time every once in a while to think about what I&#39;ve done that I&#39;m proud of, what I&#39;ve learned, and what I want to do. With the beginning of a new year comes the perfect opportunity to reflect on my life in the year&amp;nbsp;past.&lt;/p&gt;
&lt;p&gt;I&#39;ve split it into two separate blog posts, one &lt;a href=&quot;/blog/2012/year_in_review_2011_professional.html&quot;&gt;professional&lt;/a&gt; (meaning research and grad school life) and one &lt;a href=&quot;year_in_review_2011_personal.html&quot;&gt;personal&lt;/a&gt; (meaning hobbies, self-improvement, life goals). This post covers the latter; my personal life in&amp;nbsp;2011.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;This post is also split up into different sections. The first is again accomplishments, then I cover time management, my 3 main hobbies these days, and then misc other personal&amp;nbsp;stuff.&lt;/p&gt;
&lt;h3&gt;Accomplishments&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Got a bugfix patch into Hadoop. This happened at one of the Cloudera hackathons, I showed up and fixed something off JIRA, and now people are using it. It&#39;s not quite the Linux kernel patch on my &lt;a href=&quot;/bucket.html&quot;&gt;bucket list&lt;/a&gt;, but it&#39;s a&amp;nbsp;step.&lt;/li&gt;
&lt;li&gt;Bootstrapped myself as a guitarist. Literally just bought one and started playing last January, and it&#39;s worked out&amp;nbsp;okay.&lt;/li&gt;
&lt;li&gt;Became a better public speaker. I credit the two hours of teaching section every week. I&#39;m not ready to cross it off the bucket list yet (might never), but I think I&#39;ve&amp;nbsp;improved.&lt;/li&gt;
&lt;li&gt;Can order Indian food reasonably well. Credit here goes to Zaika Tuesdays last&amp;nbsp;semester.&lt;/li&gt;
&lt;li&gt;I got my RSI under control. I was actually worried in the summer that I&#39;d have to drop out of grad school and find a new profession unrelated to a keyboard, but it turned out okay after physical&amp;nbsp;therapy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Time&amp;nbsp;management&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Overall, I&#39;m reasonably happy with how much I got done. I&#39;d estimate my productivity these days at somewhere around 70-80%, and there&#39;s always going to be some slop&amp;nbsp;there.&lt;/li&gt;
&lt;li&gt;Balance. I spent last semester either working furiously or burnt out, and that&#39;s not optimal. Being burnt out basically gives you an excuse to not do anything for an indefinite amount of time, and I hate making excuses to&amp;nbsp;myself.&lt;/li&gt;
&lt;li&gt;To add to the previous point, I think having regular, recurring events each week will help me stay&amp;nbsp;sane.&lt;/li&gt;
&lt;li&gt;Intentionally scheduling my meetings early was a great way of forcing myself to work almost a&amp;nbsp;9-to-5.&lt;/li&gt;
&lt;li&gt;Getting enough sleep actually happened last semester, and it was glorious. This also goes back to not making excuses to myself (about underachieving because I&#39;m&amp;nbsp;tired).&lt;/li&gt;
&lt;li&gt;I need to make conscious decisions to do less of some things and more of others, since time is&amp;nbsp;precious.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Hobbies&lt;/h3&gt;
&lt;h4&gt;Cooking&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Cooking has tapered off as an interest, as I&#39;ve achieved some level of optimality in the &quot;effort expended vs. output achieved&quot; metric. I don&#39;t have hours every week to spend cooking up new&amp;nbsp;stuff.&lt;/li&gt;
&lt;li&gt;I still think it&#39;d be good to do something new once a week, or once a month. I&#39;m going to avoid anything elaborate or complicated though, I&#39;m past that&amp;nbsp;phase.&lt;/li&gt;
&lt;li&gt;I do want to keep cooking club going this year though, since it fell flat as soon as I got swamped with teaching and research last semester. It fulfills a social niche that isn&#39;t met by weekend parties, and it&#39;s nice to have some continuity with the same group of people.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Guitar&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Guitar-wise, I need to be more structured about how I practice. Focus on repetition until mastery, not just noodling around for an&amp;nbsp;hour.&lt;/li&gt;
&lt;li&gt;I want to make it a goal to play more with other people, since doing it over break was a lot of&amp;nbsp;fun.&lt;/li&gt;
&lt;li&gt;I&#39;m also going to make it a point to improv 12-bar blues as part of my practice&amp;nbsp;sessions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Bicycling&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Bicycling was something I missed a bunch when I was working furiously, it was a really great way of starting off my&amp;nbsp;Saturdays.&lt;/li&gt;
&lt;li&gt;I want to find some new rides, since doing 3 Bears is getting a little&amp;nbsp;repetitive.&lt;/li&gt;
&lt;li&gt;The cycling list died out a bit when I stopped organizing rides; I&#39;d like to get it to the point where it&#39;s stable without&amp;nbsp;me.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Personal&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I&#39;m not convinced I want to stick with the PhD thing all the way through yet. Grad school hasn&#39;t exactly been smooth sailing for me, but it seems to be trending upwards. This will be an important year in making a decision, since the master&#39;s is a good drop-out&amp;nbsp;point.&lt;/li&gt;
&lt;li&gt;Balance again. I still do believe in work-life integration, but when you&#39;re working 12-16 hours 6 days a week and eating out for every meal because of it, it&#39;s gone too&amp;nbsp;far.&lt;/li&gt;
&lt;li&gt;I need to write in my journal more, and write more blog posts about things I find&amp;nbsp;interesting.&lt;/li&gt;
&lt;li&gt;Strangely, I think I need to spend more money. When you&#39;re living on a grad student stipend, that little bit you could save has a high marginal utility if spent&amp;nbsp;instead.&lt;/li&gt;
&lt;li&gt;I&#39;m looking forward to TGIF with my cube from now on, the one time we did it in December before everyone left was&amp;nbsp;great.&lt;/li&gt;
&lt;/ul&gt;

   </content></entry><entry><title>Year in review: 2011 (professional)</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2012/year_in_review_2011_professional.html"/><updated>2012-01-05T21:43:00Z</updated><published>2012-01-05T21:43:00Z</published><id>http://www.umbrant.com/blog/2012/year_in_review_2011_professional.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I like to take some time every once in a while to think about what I&#39;ve done that I&#39;m proud of, what I&#39;ve learned, and what I want to do. With the beginning of a new year comes the perfect opportunity to reflect on my life in the year&amp;nbsp;past.&lt;/p&gt;
&lt;p&gt;I&#39;ve split it into two separate blog posts, one &lt;a href=&quot;/blog/2012/year_in_review_2011_professional.html&quot;&gt;professional&lt;/a&gt; (meaning research and grad school life) and one &lt;a href=&quot;year_in_review_2011_personal.html&quot;&gt;personal&lt;/a&gt; (meaning hobbies, self-improvement, life goals). This post covers the former; my life as a grad student in&amp;nbsp;2011.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;First, things I&#39;ve done in the past year that I&#39;m proud of. Next, meta-comments related to research. Finally, a section on teaching, since I TA&#39;d CS162 this past semester (my first teaching&amp;nbsp;experience).&lt;/p&gt;
&lt;h3&gt;Accomplishments&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Getting an advisor. This was really a major milestone in my grad career, and only happened as of about 4 months&amp;nbsp;ago.&lt;/li&gt;
&lt;li&gt;Publishing a paper. CrowdDB won best demo at VLDB, and PACMan got into&amp;nbsp;NSDI.&lt;/li&gt;
&lt;li&gt;Picking a good problem and starting a project I can call my own. Prior stuff was all sort of handed to me, so this is another major&amp;nbsp;milestone.&lt;/li&gt;
&lt;li&gt;Survived teaching for the first time. I feel pretty good about my own contributions to CS162; it was a lot of blood, sweat, and tears, but I love the students and all my fellow&amp;nbsp;TAs.&lt;/li&gt;
&lt;li&gt;Worked harder for a prolonged period than I ever have before. This was for Jellyfish, my class project for Ion&#39;s Cloud Computing course. Good to have done it, don&#39;t wish to repeat&amp;nbsp;it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Research-related&amp;nbsp;nuggets&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Math is really, really useful. I regret not having learned it better in undergrad, since I&#39;m pretty much resigned to taking the stat sequence here at&amp;nbsp;Berkeley.&lt;/li&gt;
&lt;li&gt;Queuing theory and control theory are basically black magic, and I don&#39;t think I&#39;ll ever grok it rigorously. Fortunately, heuristics and approximations oftentimes work, and are the common &quot;git-er-done&quot; systems&amp;nbsp;solution.&lt;/li&gt;
&lt;li&gt;Machine learning is an almost mythical solution to some problems (from my perspective). This falls in with my above two points; often I feel like there&#39;s a preexisting approach to the problem I&#39;m staring at, but even when I know vaguely what it is, it&#39;s inaccessible to mere&amp;nbsp;mortals.&lt;/li&gt;
&lt;li&gt;Start out by doing stupidly simple and unrealistic experiments to make sure you understand what&#39;s going on. We made the mistake of going directly to a very realistic workload, and got overwhelmed by the complexity. Ion very correctly identified some basic experiments that let us test our mental model of the&amp;nbsp;system.&lt;/li&gt;
&lt;li&gt;Time spent on infrastructure is not time wasted. We spent months automating our experiments, setting up clusters, scripting the graph workflow, generalizing functionality, all in the background while trying to make sense of the data. When it finally clicked, all that infrastructure allowed us to run the graphs we wanted in half a&amp;nbsp;day.&lt;/li&gt;
&lt;li&gt;Look for fundamental tradeoffs in your problem space (a Ion nugget). It can really clarify the right design&amp;nbsp;choice.&lt;/li&gt;
&lt;li&gt;Be glad when things are hard, because it means you&#39;ve chosen a good&amp;nbsp;problem.&lt;/li&gt;
&lt;li&gt;Ask questions. Yes, you might look dumb, but if you don&#39;t ask, it&#39;ll only hold you back from actually understanding what&#39;s going&amp;nbsp;on.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Teaching-related&amp;nbsp;nuggets&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I still like teaching and interacting with students, even after what has been noted as one of the more trying CS classes ever offered at UC&amp;nbsp;Berkeley.&lt;/li&gt;
&lt;li&gt;Properly preparing an hour of material takes at least two hours, and that&#39;s if you actually know as much about the topic as you think you&amp;nbsp;do.&lt;/li&gt;
&lt;li&gt;Encouraging participation during section is paramount. I think rewarding interaction with lollipops worked pretty&amp;nbsp;well.&lt;/li&gt;
&lt;li&gt;Professors are fallible, and have their own weaknesses like anyone. These weaknesses will very rarely affect their performance as a researcher, but can crop up during&amp;nbsp;teaching.&lt;/li&gt;
&lt;li&gt;Teaching can suck up time like nothing else. There&#39;s always pressure from students and instructors to spend more time on the class, and you really have to draw a line&amp;nbsp;somewhere.&lt;/li&gt;
&lt;/ul&gt;

   </content></entry><entry><title>Paper review: Facebook Haystack</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2012/haystack_review.html"/><updated>2012-01-03T15:44:00Z</updated><published>2012-01-03T15:44:00Z</published><id>http://www.umbrant.com/blog/2012/haystack_review.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;This is a review of Facebook&#39;s Haystack storage system, used to store the staggering amount of photos that are uploaded to Facebook everyday. Facebook Photos started out with an NFS appliance, but was forced to move to a custom solution for the reasons of cost, scale, and performance. Haystack is an engineering solution that applies well-known techniques from GFS and log-structured filesystems to their distributed, append-only, key-value blob situation. Metadata management is somewhat novel, as well as their CDN&amp;nbsp;integration.&lt;/p&gt;
&lt;p&gt;The paper, &quot;Finding a needle in Haystack: Facebook&#39;s photo storage&quot; by Beaver et al., was published at OSDI&amp;nbsp;&#39;10.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Main&amp;nbsp;ideas&lt;/h3&gt;
&lt;p&gt;Facebook&#39;s design requirements break down as&amp;nbsp;follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Efficient random disk access. Anything that hits Haystack missed in the CDN cache, and there are too many photos to fit it all in memory. Thus, there has to be at least one random disk seek; Haystack makes it just one (most of the&amp;nbsp;time).&lt;/li&gt;
&lt;li&gt;Efficient use of storage space. Using the normal &quot;one file per image&quot; approach killed the # of disk accesses required to serve an image, but it also required huge amounts of extraneous metadata for things like permissions and filenames that don&#39;t&amp;nbsp;matter.&lt;/li&gt;
&lt;li&gt;Append-only write semantics. Once a photo is written, it cannot be modified. Application &quot;overwrites&quot; are handled by deleting and adding a new photo with the same key. I assume that this ispretty&amp;nbsp;rare.&lt;/li&gt;
&lt;li&gt;Fault-tolerance. True of any distributed&amp;nbsp;system.&lt;/li&gt;
&lt;li&gt;Scalability and elasticity.&amp;nbsp;Ditto.&lt;/li&gt;
&lt;li&gt;Simplicity. Always good if you can get&amp;nbsp;it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Efficient disk access and use of storage space are handled by essentially keeping metadata in memory and collapsing many images together into a single file that is preallocated on disk, called a &quot;physical volume&quot;. These physical volumes are on the order of a hundred gigabytes, and function basically like segments (if you think back to the memory management part of your OS class); each photo is referenced as an offset and length within a file. As long as a server can keep this offset+length metadata and the inodes of the huge files in memory, it can do almost every photo read in a single random seek (with the corner case of falling on an extent boundary requiring two). There are also checksums and flags and other metadata that are stored in the file&amp;nbsp;on-disk.&lt;/p&gt;
&lt;p&gt;Recovery of the memory metadata is done through the use of a checkpoint file, that is then updated by examining the end of each file. This works because new photos are added sequentially, like in a log-structured filesystem. Recovering the &quot;is deleted&quot; flag is done lazily when a read request is made, by checking the flag&#39;s state on-disk (which will be right, due to all writes being synchronous). Synchronous writes and append-only semantics make consistency a&amp;nbsp;non-issue.&lt;/p&gt;
&lt;p&gt;Haystack combines multiple physical volumes into a single &quot;logical volume&quot;, through which is how photos are actually accessed. Mapping from logical to physical is again done via an in-memory structure. I think this is how they do geographic replication, by mirroring writes across all the physical volumes in a logical volume, which has to contain volumes in multiple&amp;nbsp;locations.&lt;/p&gt;
&lt;p&gt;Photo writes are optimized pretty heavily. Naturally, they are batched, but there is also some interplay with the Haystack cache (which acts like an internal CDN). Machines are marked as either write-enabled or read-only. Write-enabled machines get to keep their data in the Haystack cache, to reduce their read load moving the disk head excessively. The cache also does prefetching of new photos, since they&#39;re very likely to be accessed soon. Read-only machines with a lot of deleted photos can be compacted and (I assume) toggled back to write-enabled to receive more&amp;nbsp;photos.&lt;/p&gt;
&lt;p&gt;A few more misc&amp;nbsp;takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XFS worked the best for making the large 100GB files used for physical&amp;nbsp;volumes&lt;/li&gt;
&lt;li&gt;RAID is also used underneath, for added&amp;nbsp;availability&lt;/li&gt;
&lt;li&gt;Many layers of cache yield diminishing&amp;nbsp;returns&lt;/li&gt;
&lt;li&gt;The photo URL on FB starts off pointing at a CDN, but gets stripped down successively as it goes further into Haystack. Ex:&amp;nbsp;http://CDN/HayCache/HayMachine/VolumeAndPhoto.&lt;/li&gt;
&lt;li&gt;Centralized master maintains the logical-to-physical mapping and load balances writes across logical volumes. Seems easy to keep in memory and replicate if&amp;nbsp;necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Analysis&lt;/h3&gt;
&lt;p&gt;This really was a great application to build a new system, since existing stuff wouldn&#39;t do it as well, and the requirements made it relatively easy as distributed systems go. It pulls heavily from GFS with the centralized master and many data node approach, and also uses LFS concepts of a sequentially-written log and keeping filesystem metadata in memory. The &quot;write-enabled&quot; vs. &quot;read-only&quot; business is essentially adding journalling to a distributed filesystem, which of course is just a mini-version of the ideas in LFS. Using giant 100GB files means they were able to make their own super-simple user-level filesystem without writing an actual filesystem, a move I applaud for practical&amp;nbsp;reasons.&lt;/p&gt;
&lt;p&gt;I can&#39;t say there was much &quot;aha&quot; content in the paper though. I normally like industrial papers because of the real-world experiences, but this was a straightforward implementation paper. Their system bears many resemblances to GFS, and being tailored for a single usecase allowed them to greatly simplify the design. I&#39;m a bit disappointed, in that this is a clearly impressive system, but didn&#39;t relay any interesting&amp;nbsp;tidbits.&lt;/p&gt;

   </content></entry><entry><title>Apache Hadoop committer</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2013/apache_hadoop_committer.html"/><updated>2013-08-03T12:52:00Z</updated><published>2013-08-03T12:52:00Z</published><id>http://www.umbrant.com/blog/2013/apache_hadoop_committer.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;A quick post celebrating that I recently was made a &lt;a href=&quot;http://hadoop.apache.org/who.html&quot;&gt;committer&lt;/a&gt; on the &lt;a href=&quot;http://hadoop.apache.org/&quot;&gt;Apache Hadoop&lt;/a&gt; project. I owe a big thanks to everyone who&#39;s reviewed my patches and helped me along the way (especially my colleagues ATM, Todd, and Colin here at&amp;nbsp;Cloudera).&lt;/p&gt;
&lt;p&gt;My very first patch was &lt;a href=&quot;https://issues.apache.org/jira/browse/HDFS-1952&quot;&gt;HDFS-1952&lt;/a&gt; in May 2011, via a Hadoop hackathon hosted at Cloudera. It was the most promising &lt;a href=&quot;https://issues.apache.org/jira/issues/?jql=labels%20%3D%20newbie%20AND%20project%20%3D%22Hadoop%20HDFS%22&quot;&gt;newbie HDFS JIRA&lt;/a&gt; on the list, and I still remember all the basic issues I had checking out the repo, setting up Eclipse, using Ant, and generating the diff. Two years later, these things have gotten easier&amp;nbsp;:)&lt;/p&gt;
&lt;p&gt;Here&#39;s to many more contributions in the&amp;nbsp;future!&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>Grad school four months out</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2013/grad_school_4_months_out.html"/><updated>2013-05-12T18:02:00Z</updated><published>2013-05-12T18:02:00Z</published><id>http://www.umbrant.com/blog/2013/grad_school_4_months_out.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Here&#39;s my account of leaving the PhD program at Berkeley to work at Cloudera. My experience might not be representative or generalize beyond my own situation, but I&#39;m writing this because a number of people have asked me about the differences between grad school and industry. Choosing to leave Berkeley was a very personal decision, but fortunately I&#39;m happy with how it&#39;s turned&amp;nbsp;out.&lt;/p&gt;
&lt;p&gt;This also serves as my &quot;Year in review: 2012&quot; post, since this was the major change in my life last&amp;nbsp;year.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;Last summer, I interned with Cloudera and had a very positive experience. I clicked with the team, enjoyed working on open source and the product, and felt that I could turn some of my research into shipping production code. So, at the end of my internship, I asked for an offer and got it. Before making a decision, I talked with my advisor and my grad student friends who had previously worked in industry, but I was fairly sure I was leaving. Berkeley has a very lenient policy when it comes to this, in that all you need is your advisor&#39;s signature to leave and later your advisor&#39;s signature to return. Theoretically, my progress toward the PhD remains good forever, so I always have the option of going back (assuming Ion or another professor will take me&amp;nbsp;:).&lt;/p&gt;
&lt;p&gt;Cloudera graciously let me defer by a semester while I finished my masters, which I really appreciate, and then gave me another few weeks off in January so I could relax and go on long bike rides before starting work proper. Logistically, both Berkeley and Cloudera made the transition very easy (but no such favors from the ridiculous SF housing market,&amp;nbsp;ouch!).&lt;/p&gt;
&lt;h3&gt;Day-to-day&lt;/h3&gt;
&lt;p&gt;Day-to-day work is quite&amp;nbsp;different.&lt;/p&gt;
&lt;p&gt;In grad school, I worked fewer median hours per day (probably 7) but with high variance and more than 5 days per week. Class or paper deadlines can mean consecutive 16 hour days with no weekends, with my record being three weeks of that in a row. Not something I enjoyed, but I&#39;m glad it&#39;s something I did. On the flip side, the entire lab would empty out for a few days after a grueling deadline. I&#39;d also sometimes skip out early on a Friday if the weather was particularly nice. Taking a longer vacation consisted of telling your advisor and booking your flights. As long as it avoided any major deadlines, it was easy to take off weeks at a&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;A typical day in grad school would be getting into the lab around 10:30 or 11 (normal), talking about big research ideas with my cubicle mates for a while, and then going to some lunch seminar (either a student presentation or an external researcher). These talks were usually high quality and engaging, academics are trained how to give talks. After that, I&#39;d do some work in the afternoon (coding, reading papers, homework, more chatting about research ideas), then probably another meeting in the evening (research group, advisor, project group). Group meetings were often scheduled late since things were so hectic during the day. It wasn&#39;t uncommon to get takeout to bring back to the lab for a meeting. The first two years of grad school, your time is sliced quite finely because of classes and TA responsibilities in addition to research. It was hard to find solid blocks of time every day to do work. This gets much better in year 3 and&amp;nbsp;onward.&lt;/p&gt;
&lt;p&gt;In industry, I consistently work 5 days a week, 8-9 hours per day. Initially I was working about 8 hours per week more than this, but I was getting burned out and that wasn&#39;t doing anyone any favors. I find I&#39;m more productive on a regular schedule. Software progresses during moments of clarity, and for me, the way to maximize those moments is to stay rested and happy. One thing I do miss though is the ability to take random days off. Vacation days are now a scarce commodity and have to be planned months in advance. My commute is also about 20 minutes longer each way, which adds&amp;nbsp;up.&lt;/p&gt;
&lt;p&gt;One major Cloudera-specific bonus is that engineers get to work from home on Wednesdays. This is just enough flexibility to let me go to the dentist or get my hair cut when it&#39;s not too busy, and it&#39;s nice having a break in the middle of the week where you can code&amp;nbsp;interruption-free.&lt;/p&gt;
&lt;p&gt;A typical day for me at Cloudera is getting in around 10 or 10:30 (normal) and clearing out my email inbox. There&#39;s a lot more email that has to be sifted: bug reports, user mailing lists, JIRA traffic, on-going discussion threads. That takes about 0.5-1 hour, so I can get some coding in before lunch. Lunch is a quick affair, about 30 mins, thanks to catering delivered right to the office. There&#39;s a high probability of at least one meeting per day, otherwise it&#39;s free development time (coding, testing, email, JIRA). I normally take a break around 3 to avail myself of the free snacks and drinks. I like to leave around 6 or 7, but I&#39;ll stay longer if I have a nice flow going. After I go home though, I&#39;m usually tired and just cook dinner and&amp;nbsp;relax.&lt;/p&gt;
&lt;h3&gt;Interpersonal&lt;/h3&gt;
&lt;p&gt;Socially, grad school is like a more grown-up continuation of undergrad. Most people are in their mid-twenties, and everyone shares the qualities of being smart, motivated, and hard-working. But, it&#39;s hardly a monoculture in terms of background. People come from all over geographically, and many have worked before coming back to school. Computer science is also a very broad field; I know barely anything about graphics, AI, and theory, so it&#39;s always interesting to hear from people in those&amp;nbsp;areas.&lt;/p&gt;
&lt;p&gt;I made a lot of friends in grad school. You end up working closely with a lot of different people through group projects, and there&#39;s a lot of bonding from shared suffering around deadlines. Collaboration is heavily encouraged at Berkeley, so you meet a lot of people through talks, meetings, and random hallway chats. There&#39;s also a new crop of graduate students every year, so more people to&amp;nbsp;meet.&lt;/p&gt;
&lt;p&gt;Thus, grad school provides a nice social structure. You end up interacting with a lot of active, interesting&amp;nbsp;people.&lt;/p&gt;
&lt;p&gt;Cloudera is definitely a contrast. The biggest change is that I&#39;m interacting with far fewer people on a day-to-day basis. Fortunately I really like my coworkers, but I&#39;m interacting face-to-face with pretty much the same ~5 people every day. I&#39;ll note this is far better than other places I&#39;ve worked, where I&#39;d only &lt;em&gt;see&lt;/em&gt; 3 people the entire day, but it&#39;s not the same level of collaboration and&amp;nbsp;fraternization.&lt;/p&gt;
&lt;p&gt;I think this is just part of transitioning to working life. For a company our size, I don&#39;t think you can do better. Cloudera has put substantial effort into instilling a flat, open culture from the top down. We have a cubicle-free workspace and a single lunch room where everyone (CEOs, VPs, sales, engineering) eats. Our CEO also makes a real effort to make us all feel onboard through all-hands meetings and weekly company-wide updates, and he speaks with every new employee&amp;nbsp;one-on-one.&lt;/p&gt;
&lt;p&gt;Ultimately, this comes down to the nature of the work and differences in priorities. Software development is a more well-defined and thus less collaborative process than research. I spend comparatively more time mangling code and less time hashing out ideas on a whiteboard. Professional developers also tend to go home after 8 hours since they have responsibilities at home, while grad students generally don&#39;t and are more willing to hang&amp;nbsp;out.&lt;/p&gt;
&lt;h3&gt;Problem scope and&amp;nbsp;purpose&lt;/h3&gt;
&lt;p&gt;This gets down to why I decided to leave grad school in the first place: to be one hop closer to real-world problems, and to get perspective on what is actually important. I went to grad school directly from undergrad, so I didn&#39;t have any prior industry experience besides internships. So, to find cool research problems to work on, I&#39;d talk to people from Microsoft, or Google, or Facebook, hear what they thought was important, then try and turn that into a research paper. The main issue was that I never crisply understood all the use-cases and requirements for the systems I was building. I felt weird making assertions about how people wanted to use something, when I had never operated or used it in the real&amp;nbsp;world!&lt;/p&gt;
&lt;p&gt;This is why I chose to join Cloudera, and why Cloudera is basically the best place in the world for me to work. We&#39;re growing fast and very customer-driven when deciding what to work on. Customer needs directly guide new features and are distilled into design-time requirements. I&#39;m also forced to think more generally about the full breadth of use-cases because of the variety of our downstream users. Yes, this can be frustrating and can pollute the purity of your design, but I can be assured that I&#39;m solving a real, pressing need. I&#39;m fortunate in that I get to work on many of the same problems I was interested in at Berkeley. The separation between industry and academia isn&#39;t that large in&amp;nbsp;systems.&lt;/p&gt;
&lt;p&gt;Of course, it&#39;s not all unicorns and rainbows. While new feature development is fun and great, I spend probably 70% of my time working on bug fixes, backports, customer support tickets, and other etcetera that does not exist in grad school. There are more unplanned interruptions and my todo list is constantly backlogged. It requires strong focus and triaging skills to prioritize what&#39;s important and get things&amp;nbsp;done.&lt;/p&gt;
&lt;p&gt;I&#39;ll add that it&#39;s been a life ambition of mine to work on an open-source software platform. Originally, I thought I&#39;d go into operating systems (i.e. Linux kernel development), but I really believe it when I say Hadoop is an operating system for datacenters. That&#39;s just the coolest thing to me. We&#39;re making previously unthinkable amounts of computational resources accessible to the average programmer, solving problems that were once economically&amp;nbsp;infeasible.&lt;/p&gt;
&lt;h3&gt;Fin&lt;/h3&gt;
&lt;p&gt;There is no simple takeaway where I state that one is better than the other. I like working at Cloudera, but there&#39;s more for me to learn about research&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;The purpose of writing this post was actually two-fold. Yes, to serve as a reference for others making a similar choice, but also as a way for me to introspect and assess the outcome of my decision. External to this discussion of industry vs. academia, I&#39;m still learning how to maximize my own happiness and productivity, which happens regardless of where I&amp;nbsp;am.&lt;/p&gt;
&lt;p&gt;So, read and draw your own conclusions. I&#39;d be happy if even a single person read this and found it useful in their decision-making. I&#39;m also more than willing to talk one-on-one if you want advice; just leave a comment or shoot me an&amp;nbsp;email.&lt;/p&gt;

   </content></entry><entry><title>Hadoop 101 slides</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2013/hadoop-101-overview.html"/><updated>2013-05-02T20:25:00Z</updated><published>2013-05-02T20:25:00Z</published><id>http://www.umbrant.com/blog/2013/hadoop-101-overview.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I gave a guest lecture on the Hadoop stack last week at Tapan Parikh&#39;s &lt;a href=&quot;http://blogs.ischool.berkeley.edu/i206s13/&quot;&gt;INFO 206: Distributed Computing Applications and Infrastructure&lt;/a&gt; course at Berkeley. I took a more academic approach than most, talking about the original motivating problem of Google search before moving into a deep dive of HDFS and MapReduce and an overview of the rest of the Hadoop&amp;nbsp;ecosystem.&lt;/p&gt;
&lt;p&gt;A couple students came up afterwards to say they enjoyed the talk, so I think it was&amp;nbsp;well-received.&lt;/p&gt;
&lt;p&gt;Slides: &lt;a href=&quot;/presentations/hadoop-overview-awang-2013.pptx&quot;&gt;PPTX with animations&lt;/a&gt; and &lt;a href=&quot;/presentations/hadoop-overview-awang-2013.pdf&quot;&gt;PDF&lt;/a&gt;.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>Highly-available audio in HDFS</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2013/hdfs-player.html"/><updated>2013-04-01T18:00:00Z</updated><published>2013-04-01T18:00:00Z</published><id>http://www.umbrant.com/blog/2013/hdfs-player.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Here on the HDFS team at &lt;a href=&quot;http://www.cloudera.com/&quot;&gt;Cloudera&lt;/a&gt;, we believe in &lt;a href=&quot;http://en.wikipedia.org/wiki/Eating_your_own_dog_food&quot;&gt;eating our own dogfood&lt;/a&gt;. Since we value our (substantial) MP3 collections quite dearly, it&#39;s only natural to store them in a high performance, highly-available, enterprise-quality distributed filesystem like HDFS. Today, I&#39;m announcing the next generation in aural HDFS enjoyment: listening to music directly from the Namenode web&amp;nbsp;UI.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;It&#39;s easy to use existing music players with HDFS through &lt;a href=&quot;http://wiki.apache.org/hadoop/MountableHDFS&quot;&gt;FUSE-DFS&lt;/a&gt; or Cloudera&#39;s &lt;a href=&quot;https://github.com/cloudera/hdfs-nfs-proxy&quot;&gt;NFS proxy&lt;/a&gt;, but what if you want a zero-configuration way of listening to your tunes &lt;em&gt;now&lt;/em&gt;? This is the goal of my new pet project, &lt;code&gt;hdfs-player.js&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/umbrant/5289176&quot;&gt;Source&amp;nbsp;code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://ted.mielczarek.org/code/mozilla/bookmarklet.html&quot;&gt;Bookmarklet&amp;nbsp;maker&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Copy paste the source code into the bookmarklet maker, drag the bookmarklet into your toolbar, browse to the directory containing your music in the web UI, and prepare for excitement. Transform this boring old web&amp;nbsp;UI:&lt;/p&gt;
&lt;p&gt;&lt;img width=&quot;559px&quot; alt=&quot;Before&quot; src=&quot;hdfs-player/nn_before.png&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Into the spectacular&amp;nbsp;this:&lt;/p&gt;
&lt;p&gt;&lt;img width=&quot;559px&quot; alt=&quot;After&quot; src=&quot;hdfs-player/nn_after.png&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Use Chrome for MP3 playback, FF only supports Ogg and&amp;nbsp;wav.&lt;/p&gt;
&lt;p&gt;Happy listening,&amp;nbsp;everyone!&lt;/p&gt;

   </content></entry><entry><title>Paper review: DRAM errors in the wild</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2013/dram_errors_in_the_wild.html"/><updated>2013-02-05T22:00:00Z</updated><published>2013-02-05T22:00:00Z</published><id>http://www.umbrant.com/blog/2013/dram_errors_in_the_wild.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Today, I&#39;m looking at an excellent study by Schroeder et al., &lt;a href=&quot;http://www.cs.toronto.edu/~bianca/papers/sigmetrics09.pdf&quot;&gt;&quot;DRAM errors in the wild: A Large-Scale Field Study&quot;&lt;/a&gt;. This is the definitive paper on the subject, covering two years, thousands of machines, and millions of DIMM hours. Memory errors are particularly important in the context of growing cluster sizes; one-in-a-million errors become common at&amp;nbsp;scale.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;ECC and memory&amp;nbsp;errors&lt;/h3&gt;
&lt;p&gt;Schroeder and her Google co-authors lead with an overview of DRAM errors and the countermeasures present in today&#39;s hardware. Almost all server-grade memory is &lt;a href=&quot;http://en.wikipedia.org/wiki/ECC_memory&quot;&gt;ECC&lt;/a&gt;, meaning it can detect double-bit errors and correct single-bit errors (via something like a 7-4 Hamming code). More advanced &lt;a href=&quot;http://en.wikipedia.org/wiki/Chipkill&quot;&gt;&quot;chipkill&quot;&lt;/a&gt; schemes can also correct some multi-bit errors. These errors are detected on read; uncorrectable errors (UEs) normally result in a system reboot, while correctable errors (CEs) are fixed up on-the-go. Some systems also have a &lt;em&gt;hardware scrubber&lt;/em&gt;, which periodically checks and rewrites errors (at the rate of 1GB every 45 minutes). This is important since it can prevent correctable errors from accumulating and becoming&amp;nbsp;uncorrectable.&lt;/p&gt;
&lt;p&gt;Errors are also divided into hard and soft errors. &lt;em&gt;Soft errors&lt;/em&gt; are the famed cosmic ray flipping a bit; a random, one-off fault caused by the environment. These are the errors that countermeasures like checksums and hardware scrubbers are designed for. &lt;em&gt;Hard errors&lt;/em&gt; are structural, and more difficult to deal with. In practice, these emerge as &quot;stuck bits&quot; which can&#39;t be rewritten and fixed, and are caused by things like hardware faults or buggy&amp;nbsp;firmware.&lt;/p&gt;
&lt;h3&gt;Failure&amp;nbsp;rates&lt;/h3&gt;
&lt;p&gt;The biggest takeaway from their study is that correctable errors are far more common than previously thought. One third of machines experienced a correctable error in a year. Breaking it down, there&#39;s a 1.29% incidence per DIMM-year of a CE. Another important note is that errors are strongly correlated by node; 20% of machines cause 90% of errors. These errors are also strongly correlated by age; a DIMM that experiences 1 error in a month typically experiences 10-100 errors the following month. I think aligning with the aging hypothesis, the authors found that hard errors were much more likely than soft&amp;nbsp;errors.&lt;/p&gt;
&lt;p&gt;One surprising data point was that they did not find temperature and error rates to be correlated, once normalized for utilization. Error rates were correlated with utilization though, and utilization is correlated with temperature. I hypothesize that more heavily utilized machines are using more memory and doing more reads, and thus are more prone to errors (else, the random bit flip might just happen in an unused portion of memory and never be&amp;nbsp;encountered).&lt;/p&gt;
&lt;p&gt;The uncorrectable error rates plateau over time because Google aggressively decommissions machines that experience UEs. All said, their trace shows a 0.22% incidence per DIMM-year of a UE. Based on this, I don&#39;t think there&#39;s much danger of undetected errors (should be extremely&amp;nbsp;rare).&lt;/p&gt;
&lt;p&gt;In terms of technology, they found no differences between DDR1, DDR2, and FBDIMM. Furthermore, they did not find significant differences between manufacturers. Chipkill is seen as an extremely beneficial technology though, since hardware platforms with chipkill experienced far fewer uncorrectable&amp;nbsp;errors.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Memory errors are scary, and they happen with some frequency. However, the situation seems to be manageable. The study is a strong advocate for ECC memory, chipkill, and hardware scrubbers. Getting proper monitoring to quickly detect and decommission machines with uncorrectable errors is also important. Overall, this paper was a very readable and thorough analysis of memory errors, and will probably remain the gold standard on the topic for some&amp;nbsp;time.&lt;/p&gt;

   </content></entry><entry><title>Bucket list: Cycling a Century</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2013/bucket_list_century.html"/><updated>2013-01-18T19:28:00Z</updated><published>2013-01-18T19:28:00Z</published><id>http://www.umbrant.com/blog/2013/bucket_list_century.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I&#39;ve been taking a little time off in between transitioning from grad life at Berkeley to working full-time at Cloudera. I decided to use some of this vacation time to check off a bucket list item: bicycling an imperial century (100 miles). Here&#39;s my experience, and advice for anyone who wants to do the&amp;nbsp;same.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;The route I did is called &lt;a href=&quot;http://inl.org/cycling/rides/the-cheese-factory-century/&quot;&gt;The Cheese Factory Century&lt;/a&gt;. It&#39;s basically a tour of North Bay; across the Golden Gate Bridge, out to Tiburon, up to San Rafael, Novato, then looping back through Lucas Valley and back across the Bridge. Strava only recorded 4.4k ft of climbing, and there weren&#39;t any major climbs. I particularly enjoyed Lucas Valley; cows, rolling green hills, picturesque lakes, and not much traffic. Unfortunately I didn&#39;t have time to take any pictures, but it&#39;s pretty enough to warrant another trip. Total rolling time was 7:16, but including breaks about 9 hours. I was impressed with the route markers and availability of bike lanes; NorCal really gets this&amp;nbsp;right.&lt;/p&gt;
&lt;p&gt;This is by far the longest ride I&#39;ve ever done, which hopefully is encouraging to others. My previous distance PR was 60 miles, and adding another 40 miles didn&#39;t really change things that much physically. Last week I did a 50mi test ride of part of the route and still felt pretty good afterwards, so I decided to tackle the big&amp;nbsp;kahuna.&lt;/p&gt;
&lt;p&gt;The real challenge is in your head. I rode about 50 miles before lunch without major difficulties, but around mile 60 the self-pity really started kicking in. Miles 70-85 were probably the hardest. At that point, everything hurts, and there&#39;s not much you can do about it. Padded bike shorts only help so much, your arms ache from having held your body up for hours on end, one knee develops a twinge, and your quads feel like they&#39;re on the verge of cramping. I spent those miles ineffectually readjusting body position and grip and trying to think happy&amp;nbsp;thoughts.&lt;/p&gt;
&lt;p&gt;Around mile 85 I was out of Lucas Valley and starting to recognizing the territory (important morale boost) but those were still slow, slow miles. It was terrible to see tenths coming off my average speed, even after having banked 6 hours of effort. I stopped caring about that though around mile 95 when I realized I was going to make it, and seeing my GPS tick over to 100 right after crossing the GGB was a magical&amp;nbsp;moment.&lt;/p&gt;
&lt;h3&gt;Advice&lt;/h3&gt;
&lt;p&gt;If you want to try this yourself, the standard long-ride advice applies, just more&amp;nbsp;so.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Check your bike and check yourself. There were times when I was 15 miles from civilization, and it would have been really bad to have a breakdown or spill. My test ride let me assess my physical condition, and also helped me learn the route (really&amp;nbsp;important).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wear layers. If you&#39;re traversing 100 miles, you&#39;re going to both bake in the sun and freeze in the shade. Make sure you can adjust without&amp;nbsp;stopping.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bring lots of provisions. I ate a Clif bar, a pack of Shot Bloks, and a pack of Honey Stingers while riding. I also had 2x24oz bottles filled with watered-down Gatorade, which I refilled periodically. Still, both bottles were dry when I got home, so the extra capacity was important. Having bonked hard on a ride before, I made sure to eat and drink preemptively and often. I also had a brownie and coffee in Tiburon, and had a massive meatball sub and Gatorade for lunch in&amp;nbsp;Novato.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start early. I started just before 9AM, but better would have been 8 or even 7:30. I realized before lunch that I was going to run out of daylight. This was definitely a motivator for miles 85+ (chasing the last rays of the sun), and I managed to catch sunset right as I crossed the GGB around&amp;nbsp;5:30PM.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Stay in the drops. I observed maybe a ~2mph boost from being in the drops, which is&amp;nbsp;huge.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep pedaling. It&#39;s the only way to eat up the miles. Corollary is to avoid stopping. Personally, there was also a huge psychological benefit to having a simple mantra like this to focus on when the pain set in, e.g. miles&amp;nbsp;70+.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It&#39;s done, and I&#39;m really glad I set out to do this. It was totally different from my other athletic experiences. Crew races only last 5 or 6 minutes, and while there is a big psychological component, it&#39;s a totally different kind of hurt. My other long rides were also with groups, while this one I did solo and was much longer. Spending 9 hours in the saddle forces you to confront your physical and mental limitations again and again. This is one of the reasons I like cycling though: when it comes down to it, there&#39;s just you, your bike, and the road in front of you. No one else is going to pedal your bike for you, and the sooner that sinks in, the sooner you get up the&amp;nbsp;hill.&lt;/p&gt;

   </content></entry><entry><title>Bucket list: Catch a fish and eat it</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2014/bucket_list_fish.html"/><updated>2014-12-30T17:03:00Z</updated><published>2014-12-30T17:03:00Z</published><id>http://www.umbrant.com/blog/2014/bucket_list_fish.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I checked off one of my &lt;a href=&quot;/bucket.html&quot;&gt;bucket list&lt;/a&gt; items yesterday: catching a fish, cleaning it, and eating&amp;nbsp;it.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;This was the last day of a family vacation in Port St. Lucie in Florida.
My original plans to go deep sea fishing fell through, so I went to the surprisingly well-stocked local Walmart to pick up some freshwater gear.
I was lucky enough to nab a healthy-looking 15&quot; largemouth bass with a silver Mepps spinner from the lake behind our&amp;nbsp;timeshare.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;bucket_list_fish/fish.jpg&quot; width=&quot;559px&quot; alt=&quot;Largemouth bass&quot;/&gt;&lt;/p&gt;
&lt;p&gt;My mom, being Korean, knows a thing or two about preparing a whole fish, so I undertook the cleaning and cooking under her supervision.
We had it for dinner, and suffice to say, it was&amp;nbsp;delicious.&lt;/p&gt;

   </content></entry><entry><title>Paper review: Facebook f4</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2014/f4_facebook_warm_blob_storage.html"/><updated>2014-10-29T17:20:00Z</updated><published>2014-10-29T17:20:00Z</published><id>http://www.umbrant.com/blog/2014/f4_facebook_warm_blob_storage.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;It&#39;s been a while since I did one of these!
I did a previous review of &lt;a href=&quot;/blog/2012/haystack_review.html&quot;&gt;Facebook Haystack&lt;/a&gt;, which was designed as an online &lt;a href=&quot;https://en.wikipedia.org/wiki/Binary_large_object&quot;&gt;blob&lt;/a&gt; storage system.
f4 is a sister system that works in conjunction with Haystack, and is intended for storage of warm rather than hot blobs.
As is usual for Facebook, they came up with a system that is both eminently practical and tailored for their exact use&amp;nbsp;case.&lt;/p&gt;
&lt;p&gt;This paper, &quot;f4: Facebook&#39;s Warm BLOB Storage System&quot; by Muralidhar et al., was published at OSDI&amp;nbsp;&#39;14.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;Haystack is very good at what it was designed to do: fast random access to write-once blobs. In short, it writes out all these objects log-structured to large 100GB files on the local filesystem, and maintains an in-memory index of blob locations so it can serve up a blob with at most a single disk&amp;nbsp;seek.&lt;/p&gt;
&lt;p&gt;The downside of Haystack is that it&#39;s not very space efficient.
Files are replicated both at the node-level because of &lt;a href=&quot;https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_6&quot;&gt;RAID-6&lt;/a&gt; and also geographically three times, leading to a total replication factor of 3.6x.
f4 improves upon this by using erasure coding, which drops the replication factor to 2.1x.
Considering that Facebook has 65PB of warm blobs, we&#39;re looking at tens of PBs in savings (meaning millions of&amp;nbsp;dollars).&lt;/p&gt;
&lt;p&gt;However, the downside of erasure coding is worsened request rate and failure recovery.
With erasure coding, there&#39;s only a single data replica that can serve read requests.
Failure recovery is more expensive since it requires reading the other data and parity blocks in the stripe.
In the meanwhile, clients reads require doing online erasure coding, unless they failover to another&amp;nbsp;datacenter.&lt;/p&gt;
&lt;p&gt;It&#39;s important to note that f4 was never intended to replace Haystack, but to complement it.
They are both fronted by the same CDN, caching, and routing layers, and likely expose similar if not identical APIs.
Haystack is great for hot data, and f4 is great for warm data, the key is determining where a given blob&amp;nbsp;belongs.&lt;/p&gt;
&lt;h3&gt;Determining&amp;nbsp;hotness&lt;/h3&gt;
&lt;p&gt;Facebook&#39;s blobs tend to be accessed frequently when they&#39;re first uploaded, after which access rates drops off exponentially.
There are a couple different types of blobs, e.g. photos, videos, attachments, and each had different access rates and drop offs.
They chose to look at a nifty metric over time: 99th percentile IOPS/TB.
Based on synthetic benchmarks, they knew f4&#39;s 4TB drives could handle handle 80 IOPS with acceptable latency.
This meant a blob wamigration made sense when the IOPS/TB for a type of blob fell below&amp;nbsp;20.&lt;/p&gt;
&lt;p&gt;The other component of hotness is deletion rate.
This is important for log-structured systems since compaction (rewriting the file) is required to reclaim space from deleted items.
Fortunately, deletions illustrated the same sort of drop-off as request&amp;nbsp;rate.&lt;/p&gt;
&lt;p&gt;Profile photos, it turned out, do not exhibit a strong drop off, and are never moved to f4.
Photos ended up being hot for about 3 months, and everything else was only hot for one&amp;nbsp;month.&lt;/p&gt;
&lt;h3&gt;Storage&amp;nbsp;format&lt;/h3&gt;
&lt;p&gt;Haystack and f4 use the same concept of a &lt;em&gt;volume&lt;/em&gt; of blobs.
The volume&#39;s data file is log-structured and contains a bunch of log-structured blobs.
The volume&#39;s index file tells you how to find the blobs within the data file, without scanning the entire data file.
Once a volume grows to about 100GB, it&#39;s &lt;em&gt;locked&lt;/em&gt; and the data and index file are immutable.
At this point, the volume is a candidate for migration to&amp;nbsp;f4.&lt;/p&gt;
&lt;p&gt;One interesting note is that f4 is totally immutable, not even supporting deletes.
Through a clever trick though, it does support logical deletes.
Each blob is encrypted with a unique encryption key which is stored in an external database.
By deleting the encryption key, the blob is effectively also deleted even though the storage space is not reclaimed.
The thinking is that the delete rate is low enough that this is desirable to simplify the system.
As it turns out, only 7% of data in f4 is deleted in this manner, which isn&#39;t too bad compared to the savings from erasure coding, and considering the immense amount of data that would likely have to be&amp;nbsp;rewritten.&lt;/p&gt;
&lt;h3&gt;Erasure&amp;nbsp;coding&lt;/h3&gt;
&lt;p&gt;f4 uses Reed-Solomon (10,4) encoding, which means 10 data and 4 parity blocks in a stripe, and can thus tolerate losing up to 4 blocks before they lose the entire stripe.
They also use XOR encoding for geographic replication, doing essentially XOR (2,1) encoding across three datacenters.
So, their replication factor is 1.4 for the RS (10,4), 1.5 for the XOR (2,1), for a total of 2.1x.
Before introducing XOR encoding, they were doing simple 2x replication, so some of their data is still encoded at&amp;nbsp;2.8x.&lt;/p&gt;
&lt;p&gt;Because single data files are so large, f4 can use a large block size of 1GB, and use the ~100 blocks to form stripes.
There&#39;s no need to stripe at a finer granularity like QFS.
It&#39;s not clear whether they inline the parity blocks or write them to a side file, but I think it&#39;d be nifty to bulk-import the data and index files directly from Haystack and then just add the parity file&amp;nbsp;later.&lt;/p&gt;
&lt;h3&gt;System&amp;nbsp;architecture&lt;/h3&gt;
&lt;p&gt;Architecturally, f4 bears a strong resemblance to HDFS, which isn&#39;t surprising since it&#39;s built on top of it.
Their name node is highly-available and maintains the mapping between blocks and storage nodes which have the block (the data node equivalent).
Storage nodes store and serve the actual data files and index files for volumes.
In a diversion from HDFS though, the name node also distributes this mapping to the storage nodes, and in fact clients issue reads and writes directly to storage nodes.
This is great because it better distributes load, but does not actually save an RPC since the client still needs to go to the storage node that has the desired&amp;nbsp;blob.&lt;/p&gt;
&lt;p&gt;A lot of the additional functionality was also built out as external services that can run on storage-less, CPU-heavy&amp;nbsp;machines.&lt;/p&gt;
&lt;p&gt;When online reconstruction is required to serve a client request, this task is handled by a &lt;em&gt;backoff node&lt;/em&gt; which issues offset reads to the other blocks in the stripe, and reconstructs from the first ones that come back.
This involves only recovering a single blob, not the entire&amp;nbsp;block.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Rebuilder nodes&lt;/em&gt; are the counterpart to backoff nodes, and handle background erasure coding.
They throttle themselves heavily to avoid affecting foreground client requests.
These nodes are actually also responsible for probing for node failures, and report failures to &lt;em&gt;coordinator nodes&lt;/em&gt;, which, as it sounds, coordinate recovery tasks. These coordinators also handle fixing up placement violations, if multiple blocks from a single stripe end up on the same&amp;nbsp;rack.&lt;/p&gt;
&lt;p&gt;f4 basically glues a new set of soft-state coordinators and workers onto HDFS, rather than baking the functionality into the existing NN and DN.
These services likely still require talking to the NN, but this is okay since NNs are not heavily loaded since client load is being handled by storage nodes. This is not true of us, so performance is a real concern, and we typically shy away from the operational complexity of new services since most of our customers are not as sophisticated as Facebook&#39;s ops&amp;nbsp;team.&lt;/p&gt;
&lt;h3&gt;Hardware&amp;nbsp;notes&lt;/h3&gt;
&lt;p&gt;The big overall theme here is separation of storage, compute, and cache.
Storage nodes get to focus only on storage, since the compute-heavy tasks are handled by the new separate services, and the entire blob store is already fronted by multiple levels of caching (so no need for buffer cache).
Storage nodes are thus very CPU light, and only need enough memory to be able to fit the blob index and block-node mapping into memory.
The index size seems to dominate, and they mention potentially storing it on&amp;nbsp;SSDs.&lt;/p&gt;
&lt;p&gt;One other fun note is that they wanted to further downsize their CPUs to save on power, but were unable to do so without sacrificing memory capacity.
Intel probably doesn&#39;t want to cannibalize their high-end server market.
It&#39;s also hard to find support for ECC memory in lower-end&amp;nbsp;processors.&lt;/p&gt;
&lt;p&gt;They run with a single cluster configuration: 30 4TB drives per 2U host, 14 racks of 15 hosts per cell.
Total unformatted capacity per cell is thus 25PB.
14 is the minimum number of racks for RS (10,4) encoding to protect against rack failures. Running with the minimum means a single failure means you can&#39;t get back up to full strength, but since they&#39;re also doing geographic replication this is kind of&amp;nbsp;okay.&lt;/p&gt;
&lt;p&gt;Somewhat obvious, but their network must be fast and plentiful, or else they wouldn&#39;t be doing erasure coding at all.
It&#39;s great to get additional confirmation that this can be done at&amp;nbsp;scale.&lt;/p&gt;
&lt;p&gt;Finally, doing the above trace-driven IOPS/TB analysis also let them do hardware provisioning based on their SLOs.
They provisioned f4 such that the weekly peak load on any drive is less than the maximum IOPS it can deliver.
Those are some pretty strong guarantees on high-percentile&amp;nbsp;latency.&lt;/p&gt;
&lt;h3&gt;Other misc&amp;nbsp;notes&lt;/h3&gt;
&lt;p&gt;Constraints leveraged to simplify the design&amp;nbsp;space:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One&amp;nbsp;workload&lt;/li&gt;
&lt;li&gt;One node, rack, and cluster&amp;nbsp;configuration&lt;/li&gt;
&lt;li&gt;One file size (~100GB), and it&#39;s nice and&amp;nbsp;large&lt;/li&gt;
&lt;li&gt;Only have full 1GB&amp;nbsp;blocks&lt;/li&gt;
&lt;li&gt;No appends or&amp;nbsp;deletions&lt;/li&gt;
&lt;li&gt;Fixed erasure coding&amp;nbsp;scheme&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From the HDFS developer&#39;s&amp;nbsp;perspective:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Having the block-to-node mapping on their storage nodes is interesting, but probably only possible since their metadata is relatively small. 25PB / 1GB = 25 million blocks, which is pretty manageable compared to some clusters we&amp;nbsp;see.&lt;/li&gt;
&lt;li&gt;Since their recovery nodes are external to the NN, they probably have some way of writing a new block directly to a storage node without going through the NN, or a concat-like API that lets them slide a new block into an existing&amp;nbsp;file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It&#39;s great to see a paper that opens with a data-driven analysis of their target workload.
They clearly spent a lot of time gathering traces, doing analysis, and running synthetic workloads.
The end result was a system that works well in production, in tandem with their other&amp;nbsp;systems.&lt;/p&gt;
&lt;p&gt;Although f4 is specifically tailored for Facebook&#39;s environment, I find it very heartening that they built it on top of their forked version of HDFS.
We&#39;re currently working on &lt;a href=&quot;https://issues.apache.org/jira/browse/HDFS-7285&quot;&gt;erasure coding for upstream HDFS&lt;/a&gt;, and I&#39;m sure our design will differ substantially from f4, but identifying why we make different design choices will be interesting in and of&amp;nbsp;itself.&lt;/p&gt;

   </content></entry><entry><title>In-memory Caching in HDFS: Lower latency, same great taste</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2014/in_memory_caching_in_hdfs.html"/><updated>2014-05-03T18:27:00Z</updated><published>2014-05-03T18:27:00Z</published><id>http://www.umbrant.com/blog/2014/in_memory_caching_in_hdfs.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;My coworker Colin McCabe and I recently gave a talk at &lt;a href=&quot;http://hadoopsummit.org/amsterdam/&quot;&gt;Hadoop Summit Amsterdam&lt;/a&gt; titled &quot;In-memory Caching in HDFS: Lower latency, same great taste.&quot; I&#39;m very pleased with how this feature turned out, since it was approximately a year-long effort going from initial design to production system. Combined with Impala, we showed up to a 6x performance improvement by running on cached data, and that number will only improve with time. &lt;a href=&quot;/presentations/in-memory-caching-in-hdfs-awang-cmccabe-2014-03-26.pptx&quot;&gt;Slides&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/watch?v=t4AyNZMCSTw&amp;amp;list=PLSAiKuajRe2kIxG-WKmTOZNlDpSgYwEBs&amp;amp;index=10&quot;&gt;video of our presentation&lt;/a&gt; are available&amp;nbsp;online.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>Two engineering principles</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2014/two_engineering_principles.html"/><updated>2014-01-08T13:15:00Z</updated><published>2014-01-08T13:15:00Z</published><id>http://www.umbrant.com/blog/2014/two_engineering_principles.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I received two interesting pieces of advice at the AMP Lab retreat this past week, which concisely state some of my favorite software engineering&amp;nbsp;principles:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Don&#39;t be a zealot. Understand in technical detail &lt;em&gt;why&lt;/em&gt; a given language, framework, or design should be preferred, not because of technological fascination or fanboy-ism. The canonical examples here are programming language flamewars, e.g. Java vs.&amp;nbsp;C++.&lt;/li&gt;
&lt;li&gt;Ruthlessly optimize for your requirements. This means first, carefully defining said requirements, but then being completely unafraid to buck conventional wisdom if it&#39;s not a good match. This often means intentionally pruning out features, even common ones implemented by other&amp;nbsp;systems.&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>Mesos, Omega, Borg: A Survey</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2015/mesos_omega_borg_survey.html"/><updated>2015-05-27T13:57:00Z</updated><published>2015-05-27T13:57:00Z</published><id>http://www.umbrant.com/blog/2015/mesos_omega_borg_survey.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Google recently unveiled one of their crown jewels of system infrastructure: &lt;a href=&quot;http://research.google.com/pubs/pub43438.html&quot;&gt;Borg&lt;/a&gt;, their cluster scheduler.
This prompted me to re-read the &lt;a href=&quot;https://www.cs.berkeley.edu/~alig/papers/mesos.pdf&quot;&gt;Mesos&lt;/a&gt; and &lt;a href=&quot;http://research.google.com/pubs/pub41684.html&quot;&gt;Omega&lt;/a&gt; papers, which deal with the same topic.
I thought it&#39;d be interested to do a compare and contrast of these systems.
Mesos gets credit for the groundbreaking idea of two-level scheduling, Omega improved upon this with an analogy from databases, and Borg can sort of be seen as the culmination of all these&amp;nbsp;ideas.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;Cluster schedulers have existed long before big data. There&#39;s a rich literature on scheduling on 1000s of cores in the HPC world, but their problem domain is simpler than what is addressed by &lt;em&gt;datacenter schedulers&lt;/em&gt;, meaning Mesos/Borg and their ilk. Let&#39;s compare and contrast on a few&amp;nbsp;dimensions.&lt;/p&gt;
&lt;h4&gt;Scheduling for&amp;nbsp;locality&lt;/h4&gt;
&lt;p&gt;Supercomputers separate storage and compute and connect them with an approximately full-bisection bandwidth network that goes at close to memory speeds (GB/s). This means your tasks can get placed anywhere on the cluster without worrying much about locality, since all compute nodes can access data equally quickly. There are a few hyper-optimized applications that optimize for the network topology, but these are very&amp;nbsp;rare.&lt;/p&gt;
&lt;p&gt;Data center schedulers &lt;em&gt;do&lt;/em&gt; care about locality, and in fact this is the whole point of GFS and MapReduce co-design. Back in the 2000s, network bandwidth was comparatively much more expensive than disk bandwidth. So, there was a huge economic savings by scheduling your computation tasks on the same node that held the data. This is a major scheduling constraint; whereas before you could put the task anywhere, now it needs to go on one of the three data&amp;nbsp;replicas.&lt;/p&gt;
&lt;h4&gt;Hardware&amp;nbsp;configuration&lt;/h4&gt;
&lt;p&gt;Supercomputers are typically composed of homogeneous nodes, i.e. they all have the same hardware specs. This is because supercomputers are typically purchased in one shot: a lab gets $x million dollars for a new one, and they spend it all upfront. Some HPC applications are optimized for the specific CPU models in a supercomputer. New technology like GPUs or co-processors are rolled out as a new&amp;nbsp;cluster.&lt;/p&gt;
&lt;p&gt;In the big data realm, clusters are primarily storage constrained, so operators are continually adding new racks with updated specs to expand cluster capacity. This means it&#39;s typical for nodes to have different CPUs, memory capacities, number of disks, etc. Also toss in special additions like SSDs, GPUs, shingled drives. A single datacenter might need to support a broad range of applications, and all of this again imposes additional scheduling&amp;nbsp;constraints.&lt;/p&gt;
&lt;h4&gt;Queue management and&amp;nbsp;scheduling&lt;/h4&gt;
&lt;p&gt;When running an application on a supercomputer, you specify how many nodes you want, the queue you want to submit your job to, and how long the job will run for. Queues place different restrictions on how many resources you can request and how long your job can run for. Queues also have a priority or reservation based system to determine ordering. Since the job durations are all known, this is a pretty easy box packing problem. If the queues are long (typically true) and there&#39;s a good mix of small jobs to backfill the space leftover from big jobs (also typical), you can achieve extremely high levels of utilization. I like to visualize this in 2D, with time as X and resource usage as&amp;nbsp;Y.&lt;/p&gt;
&lt;p&gt;As per the previous, datacenter scheduling is a more general problem. The &quot;shape&quot; of resource requests can be quite varied, and there are more dimensions. Jobs also do not have a set duration, so it&#39;s hard to pre-plan queues. Thus we have more sophisticated scheduling algorithms, and the performance of the scheduler thus becomes&amp;nbsp;important.&lt;/p&gt;
&lt;p&gt;Utilization as a general rule is going to be worse (unless you&#39;re Google; more on that later), but one benefit over HPC workloads is that MapReduce and similar can be incrementally scheduled instead of gang scheduled. HPC, we wait until all N nodes that you requested are available, then run all your tasks at once. MR can instead run its tasks in multiple waves, meaning it can still effectively use bits of leftover resources. A single MR job can also ebb and flow based on cluster demand, which avoids the need for preemption or resource reservations, and also helps with fairness between multiple&amp;nbsp;users.&lt;/p&gt;
&lt;h3&gt;Mesos&lt;/h3&gt;
&lt;p&gt;Mesos predates YARN, and was designed with the problems of the original MapReduce in mind. Back then, Hadoop clusters could run only a single application: MapReduce. This made it difficult to run applications that didn&#39;t conform to a map phase followed by a reduce phase. The biggest example here is Spark. Previously, you&#39;d have to install a whole new set of workers and masters for Spark, which would sit alongside your MapReduce workers and masters. Hardly ideal from a utilization perspective, since they were typically statically&amp;nbsp;partitioned.&lt;/p&gt;
&lt;p&gt;Mesos addresses this problem by providing a generalized scheduler for all cluster applications. MapReduce and Spark became simply different applications using the same underlying resource sharing framework. The simplest approach would be to write a centralized scheduler, but that has a number of&amp;nbsp;drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API complexity. We need a single API that is a superset of all known framework scheduler APIs. This is difficult by itself. Expressing resource requests will also become very&amp;nbsp;complicated.&lt;/li&gt;
&lt;li&gt;Performance. 10&#39;s of thousands of nodes and millions of tasks is a lot, especially if the scheduling problem is&amp;nbsp;complex.&lt;/li&gt;
&lt;li&gt;Code agility. New schedulers and new frameworks are constantly being written, with new&amp;nbsp;requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, Mesos introduces the idea of &lt;em&gt;two-level scheduling&lt;/em&gt;. Mesos delegates the per-application scheduling work to the applications themselves, while Mesos still remains responsible for resource distribution between applications and enforcing overall fairness. This means Mesos can be pretty thin, 10K lines of&amp;nbsp;code.&lt;/p&gt;
&lt;p&gt;Two-level scheduling happens through a novel API called &lt;em&gt;resource offers&lt;/em&gt;, where Mesos periodically offers some resources to the application schedulers. This sounds backwards at first (the request goes from the master to the application?), but it&#39;s actually not that strange. In MR1, the TaskTracker workers are the source of truth as to what&#39;s running on a node. When a TT heartbeats in saying that a task has completed, the JobTracker then chooses something else to run on that TaskTracker. Scheduling decisions are triggered by what&#39;s essentially a resource offer from the worker. In Mesos, the resource offer comes from the Mesos master instead of the slave, since Mesos is managing the cluster. Not that&amp;nbsp;different.&lt;/p&gt;
&lt;p&gt;Resource offers act as time-bounded leases for some resources. Mesos offers resources to an application based on policies like priority or fair share. The app then computes how it uses them, and tells Mesos what resources from the offer it wants. This gives the app lots of flexibility, since it can choose to run a portion of tasks now, wait for a bigger allocation later (gang scheduling), or size its tasks differently to fit what&#39;s available. Since offers are time-bounded, it also incentivizes applications to schedule&amp;nbsp;quickly.&lt;/p&gt;
&lt;p&gt;Some concerns and how they were&amp;nbsp;addressed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Long tasks hogging resources. Mesos lets you reserve some resources for short tasks, killing them after a time limit. This also incentivizes using short tasks, which is good for&amp;nbsp;fairness.&lt;/li&gt;
&lt;li&gt;Performance isolation. Use Linux Containers&amp;nbsp;(cgroups).&lt;/li&gt;
&lt;li&gt;Starvation of large tasks. It&#39;s difficult to get sole access to a node, since some other app with smaller tasks will snap it up. The fix is having a &lt;em&gt;minimum offer size&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unaddressed / unknown&amp;nbsp;resolution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gang scheduling. I think this is impossible to do with high utilization without either knowing task lengths or preempting. Incrementally hoarding resources works with low utilization, but can result in&amp;nbsp;deadlock.&lt;/li&gt;
&lt;li&gt;Cross-application preemption is also hard. The resource offer API has no way of saying &quot;here are some low-priority tasks I could kill if you want them&quot;. Mesos depends on tasks being short to achieve&amp;nbsp;fairness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Omega&lt;/h3&gt;
&lt;p&gt;Omega is sort of a successor to Mesos, and in fact shares an author. Since the paper uses simulated results for its evaluation, I suspect it never went into production at Google, and the ideas were rolled into the next generation of Borg. Rewriting the API is probably too invasive of a change, even for&amp;nbsp;Google.&lt;/p&gt;
&lt;p&gt;Omega takes the resource offers one degree further. In Mesos, resource offers are &lt;em&gt;pessimistic&lt;/em&gt; or &lt;em&gt;exclusive&lt;/em&gt;. If a resource has been offered to an app, the same resource won&#39;t be offered to another app until the offer times out. In Omega, resource offers are &lt;em&gt;optimistic&lt;/em&gt;. Every application is offered all the available resources on the cluster, and conflicts are resolved at commit time. Omega&#39;s resource manager is essentially just a relational database of all the per-node state with different types of optimistic concurrency control to resolve conflicts. The upside of this is vastly increased scheduler performance (full parallelism) and better&amp;nbsp;utilization.&lt;/p&gt;
&lt;p&gt;The downside of all this is that applications are in a free-for-all where they are allowed to gobble up resources as fast as they want, and even preempt other users. This is okay for Google because they use a priority-based system, and can go yell at their internal users. Their workload broadly falls into just two priority bands: high-priority &lt;em&gt;service&lt;/em&gt; jobs (HBase, webservers, long-lived services) and low-priority &lt;em&gt;batch&lt;/em&gt; jobs (MapReduce and similar). Applications are allowed to preempt lower-priority jobs, and are also trusted to stay within their cooperatively enforced limits on # of submitted jobs, amount of allocated resources, etc. I think Yahoo has said differently about being able to go yell at users (certainly not scalable), but it works somehow at&amp;nbsp;Google.&lt;/p&gt;
&lt;p&gt;Most of the paper talks about how this optimistic allocation scheme works with conflicts, which is always the question. There are a few high-level&amp;nbsp;notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Service jobs are larger, and have more rigorous placement requirements for fault-tolerance (spread across&amp;nbsp;racks).&lt;/li&gt;
&lt;li&gt;Omega can probably scale up to 10s but not 100s of schedulers, due to the overhead of distributing the full cluster&amp;nbsp;state.&lt;/li&gt;
&lt;li&gt;Scheduling times of a few seconds is typical. They also compare up to 10s and 100s of seconds, which is where the benefits of two-level scheduling really kick in. Not sure how common this is, maybe for service&amp;nbsp;jobs?&lt;/li&gt;
&lt;li&gt;Typical cluster utilization is about&amp;nbsp;60%.&lt;/li&gt;
&lt;li&gt;Conflicts are rare enough that OCC works in practice. They were able to go up to 6x their normal batch workload before the scheduler fell&amp;nbsp;apart.&lt;/li&gt;
&lt;li&gt;Incremental scheduling is very important. Gang-scheduling is significantly more expensive to implement due to increased conflicts. Apparently most applications can do incremental okay, and can just do a couple partial allocations to get up to their total desired&amp;nbsp;amount.&lt;/li&gt;
&lt;li&gt;Even for complicated schedulers (10s per-job overheads), Omega can still schedule a mixed workload with reasonable wait&amp;nbsp;times.&lt;/li&gt;
&lt;li&gt;Experimenting with a new MapReduce scheduler was empirically easy with&amp;nbsp;Omega&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Open&amp;nbsp;questions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;At some point, optimistic concurrency control breaks down because of a high conflict rate and the duplicated work from retries. It seems like they won&#39;t run into this in practice, but I wonder if there are worst-case scenarios with oddly-shaped tasks. Is this affected by the mix of service and batch jobs? Is this something that is tuned in&amp;nbsp;practice?&lt;/li&gt;
&lt;li&gt;Is a lack of global policies really acceptable? Fairness, preemption,&amp;nbsp;etc.&lt;/li&gt;
&lt;li&gt;What&#39;s the scheduling time like for different types of jobs? Have people written very complicated&amp;nbsp;schedulers?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Borg&lt;/h3&gt;
&lt;p&gt;This is a production experience paper. It&#39;s the same workload as Omega since it&#39;s also Google, so many of the metapoints are the&amp;nbsp;same.&lt;/p&gt;
&lt;h4&gt;High-level&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Everything runs within Borg, including the storage systems like CFS and&amp;nbsp;BigTable.&lt;/li&gt;
&lt;li&gt;Median cluster size is 10K nodes, though some are much&amp;nbsp;bigger.&lt;/li&gt;
&lt;li&gt;Nodes can be very&amp;nbsp;heterogeneous.&lt;/li&gt;
&lt;li&gt;Linux process isolation is used (essentially containers), since Borg predates modern virtual machine infrastructure. Efficiency and launch time were&amp;nbsp;important.&lt;/li&gt;
&lt;li&gt;All jobs are statically linked&amp;nbsp;binaries.&lt;/li&gt;
&lt;li&gt;Very complicated, very rich resource specification language&amp;nbsp;available&lt;/li&gt;
&lt;li&gt;Can rolling update running jobs, meaning configuration and binary. This sometimes requires a task restart, so fault-tolerance is&amp;nbsp;important.&lt;/li&gt;
&lt;li&gt;Support for &quot;graceful stop&quot; via SIGTERM before final kill via SIGKILL. The soft kill is optional, and can not be relied on for&amp;nbsp;correctness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Allocs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Resource allocation is separated from process liveness. An &lt;em&gt;alloc&lt;/em&gt; can be used for task grouping or to hold resources across task&amp;nbsp;restarts.&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;alloc set&lt;/em&gt; is a group of allocs on multiple machines. Multiple jobs can be run within a single&amp;nbsp;alloc.&lt;/li&gt;
&lt;li&gt;This is actually a pretty common pattern! Multi-process is useful to separate concerns and&amp;nbsp;development.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Priorities and&amp;nbsp;quotas&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Two priority bands: high and low for service and&amp;nbsp;batch.&lt;/li&gt;
&lt;li&gt;Higher priority jobs can preempt lower&amp;nbsp;priority&lt;/li&gt;
&lt;li&gt;High priority jobs cannot preempt each other (prevents cascading livelock&amp;nbsp;situations)&lt;/li&gt;
&lt;li&gt;Quotas are used for admission control. Users pay more for quota at higher&amp;nbsp;priorities.&lt;/li&gt;
&lt;li&gt;Also provide a &quot;free&quot; tier that runs at lowest priority, to encourage high utilization and backfill&amp;nbsp;work.&lt;/li&gt;
&lt;li&gt;This is a simple and easy to understand&amp;nbsp;system!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Scheduling&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Two phases to scheduling: finding feasible nodes, then scoring these nodes for final&amp;nbsp;placement.&lt;/li&gt;
&lt;li&gt;Feasibility is heavily determined by task&amp;nbsp;constraints.&lt;/li&gt;
&lt;li&gt;Scoring is mostly determined by system properties, like best-fit vs. worst-fit, job mix, failure domains, locality,&amp;nbsp;etc.&lt;/li&gt;
&lt;li&gt;Once final nodes are chosen, Borg will preempt to fit if&amp;nbsp;necessary.&lt;/li&gt;
&lt;li&gt;Typical scheduling time is around 25s, because of localizing dependencies. Downloading the binaries is 80% of this. This locality matters. Torrent and tree protocols are used to distribute&amp;nbsp;binaries.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Scalability&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Centralization has not been an impossible performance&amp;nbsp;bottleneck.&lt;/li&gt;
&lt;li&gt;10s of thousands of nodes, 10K tasks per minute scheduling&amp;nbsp;rate.&lt;/li&gt;
&lt;li&gt;Typical Borgmaster uses 10-14 cores and 50GB of&amp;nbsp;RAM.&lt;/li&gt;
&lt;li&gt;Architecture has become more and more multi-process over time, with reference to Omega and two-level&amp;nbsp;scheduling.&lt;/li&gt;
&lt;li&gt;Single master Borgmaster, but some responsibilities are still sharded: state updates from workers, read-only&amp;nbsp;RPCs.&lt;/li&gt;
&lt;li&gt;Some obvious optimizations: cache machine scores, compute feasibility once per task type, don&#39;t attempt global optimality when making scheduling&amp;nbsp;decisions.&lt;/li&gt;
&lt;li&gt;Primary argument against bigger cells is isolation from operator errors and failure propagation. Architecture keeps scaling&amp;nbsp;fine&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Utilization&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Their primary metric was &lt;em&gt;cell compaction&lt;/em&gt;, or the smallest cluster that can still fit a set of tasks. Essentially box&amp;nbsp;packing.&lt;/li&gt;
&lt;li&gt;Big gains from the following: not segregating workloads or users, having big shared clusters, fine-grained resource&amp;nbsp;requests.&lt;/li&gt;
&lt;li&gt;Optimistic overcommit on a per-Borglet basis. Borglets do resource estimation, and backfill non-prod work. If the estimation is incorrect, kill off the non-prod work. Memory is the inelastic&amp;nbsp;resource.&lt;/li&gt;
&lt;li&gt;Sharing does not drastically affect CPI (CPU interference), but I wonder about the effect on&amp;nbsp;storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Lessons&amp;nbsp;learned&lt;/h4&gt;
&lt;p&gt;The issues listed here are pretty much fixed in Kubernetes, their public, open-source container&amp;nbsp;scheduler.&lt;/p&gt;
&lt;p&gt;Bad:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Would be nice to schedule multi-job workflows rather than single joba, for tracking and management. This also requires more flexible ways of referring to components of a workflow. This is solved by attaching arbitrary key-value pairs to each task and allowing users to query against&amp;nbsp;them.&lt;/li&gt;
&lt;li&gt;One IP per machine. This leads to port conflicts on a single machine and complicates binding and service discovery. This is solved by Linux namespaces, IPv6,&amp;nbsp;SDN.&lt;/li&gt;
&lt;li&gt;Complicated specification language. Lots of knobs to turn, which makes it hard to get started as a casual user. Some work on automatically determining resource&amp;nbsp;requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allocs are great! Allows helper services to be easily placed next to the main&amp;nbsp;task.&lt;/li&gt;
&lt;li&gt;Baking in services like load balancing and naming is very&amp;nbsp;useful.&lt;/li&gt;
&lt;li&gt;Metrics, debugging, web UIs are very important so users can solve their own&amp;nbsp;problems.&lt;/li&gt;
&lt;li&gt;Centralization scales up well, but need to split it up into multiple processes. Kubernetes does this from the start, meaning a nice clean API between the different scheduler&amp;nbsp;components.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Closing&amp;nbsp;remarks&lt;/h3&gt;
&lt;p&gt;It seems like YARN will need to draw from Mesos and Omega to scale up to the 10K node scale. YARN is still a centralized scheduler, which is the strawman for comparison in Mesos and Omega. Borg specifically mentions the need to shard to&amp;nbsp;scale.&lt;/p&gt;
&lt;p&gt;Isolation is very important to achieve high utilization without compromising SLOs. This can surface at the application layer, where apps themselves need to be design to be latency-tolerant. Think tail-at-scale request replication in BigTable. Ultimately it comes down to hardware spend vs. software spend. Running at lower utilization sidesteps this problem. Or, you can tackle it head-on through OS isolation mechanisms, resource estimation, and tuning your workload and schedulers. At Google-scale, there&#39;s enough hardware that it makes sense to hire a bunch of kernel developers. Fortunately they&#39;ve done the work for us&amp;nbsp;:)&lt;/p&gt;
&lt;p&gt;I wonder also if the Google workload assumptions apply more generally. Priority bands, reservations, and preemption work well for Google, but our customers almost all use the fair share scheduler. Yahoo uses the capacity scheduler. Twitter uses the fair scheduler. I haven&#39;t heard of any demand or usage of a priority + reservation&amp;nbsp;scheduler.&lt;/p&gt;
&lt;p&gt;Finally, very few of our customers run big shared clusters as envisioned at Google. We have customers with thousands of nodes, but this is split up into pods of hundreds of nodes. It&#39;s also still common to have separate clusters for separate users or applications. Clusters are also typically homogeneous in terms of hardware. I think this will begin to change though, and&amp;nbsp;soon.&lt;/p&gt;

   </content></entry><entry><title>Transparent encryption in HDFS</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2015/transparent_encryption_in_hdfs.html"/><updated>2015-05-27T12:34:00Z</updated><published>2015-05-27T12:34:00Z</published><id>http://www.umbrant.com/blog/2015/transparent_encryption_in_hdfs.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I went on a little European roadshow last month, presenting my recent work on transparent encryption in HDFS at &lt;a href=&quot;http://2015.hadoopsummit.org/brussels/&quot;&gt;Hadoop Summit Brussels&lt;/a&gt; and &lt;a href=&quot;http://strataconf.com/big-data-conference-uk-2015/public/schedule/detail/39913&quot;&gt;Strata Hadoop World London&lt;/a&gt;.
I&#39;ll also be giving the same talk this fall at Strata Hadoop World NYC, which will possibly be the biggest audience I&#39;ve ever spoken in front&amp;nbsp;of.&lt;/p&gt;
&lt;p&gt;Slides: &lt;a href=&quot;/presentations/transparent_encryption_hdfs_clamb_awang-2015-04-28.pptx&quot;&gt;pptx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video: &lt;a href=&quot;https://www.youtube.com/watch?v=rTiEcRwy5G4&quot;&gt;Hadoop Summit Brussels&amp;nbsp;(youtube)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you have access to O&#39;Reilly, there should be a higher quality video available&amp;nbsp;there.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>The Next Generation of Apache Hadoop</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2016/next_generation_of_apache_hadoop.html"/><updated>2016-08-25T21:44:00Z</updated><published>2016-08-25T21:44:00Z</published><id>http://www.umbrant.com/blog/2016/next_generation_of_apache_hadoop.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;Apache Hadoop turned ten this year. To celebrate, Karthik and I gave a talk at USENIX ATC &#39;16 about open problems to solve in Hadoop&#39;s second decade.
This was an opportunity to revisit our academic roots and get a new crop of graduate students interested in the real distributed systems problems we&#39;re trying to solve in&amp;nbsp;industry.&lt;/p&gt;
&lt;p&gt;This is a huge topic and we only had a 25 minute talk slot, so we were pitching problems rather than solutions.
However, we did have some ideas in our back pocket, and the hallway track and birds-of-a-feather we hosted afterwards led to a lot of good&amp;nbsp;discussion.&lt;/p&gt;
&lt;p&gt;Karthik and I split up the content thematically, which worked really well.
I covered scalability, meaning sharded filesystems and federated resource management.
Karthik addressed scheduling (unifying batch jobs and long-running services) and utilization (overprovisioning, preemption,&amp;nbsp;isolation).&lt;/p&gt;
&lt;p&gt;I&#39;m hoping to give this talk again in longer form, since I&#39;m proud of the&amp;nbsp;content.&lt;/p&gt;
&lt;p&gt;Slides: &lt;a href=&quot;/presentations/atc_2016.pptx&quot;&gt;pptx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.usenix.org/conference/atc16/technical-sessions/presentation/kambatla&quot;&gt;USENIX site with PDF slides and&amp;nbsp;audio&lt;/a&gt;&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;p&gt;Talking big ideas like this with Karthik also made me nostalgic for graduate school.
Karthik is one of the most impressive people I know; I thought he&#39;d left graduate school for Cloudera like me, but he&#39;s actually been working on his PhD nights and weekends!
While we were prepping this presentation for ATC, he was also working on a submission for SoCC, and is apparently close to&amp;nbsp;graduating.&lt;/p&gt;

   </content></entry><entry><title>Distributed Testing</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2016/distributed_testing.html"/><updated>2016-08-25T20:46:00Z</updated><published>2016-08-25T20:46:00Z</published><id>http://www.umbrant.com/blog/2016/distributed_testing.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;I gave a presentation titled &lt;a href=&quot;https://apachebigdata2016.sched.org/event/0b593f209a2ca2cfef189069db3a085c?iframe=no&quot;&gt;Happier Developers and Happier Software through Distributed Testing&lt;/a&gt; at Apache Big Data 2016, which detailed how our distributed unit testing framework has decreased the runtime of Apache Hadoop&#39;s unit test suite by &lt;strong&gt;60x&lt;/strong&gt; from 8.5 hours to about 8 minutes, and the substantial productivity improvements that are possible when developers can easily run and interact with the test&amp;nbsp;suite.&lt;/p&gt;
&lt;p&gt;The infrastructure is general enough to accommodate any software project.
We wrote frontends for both C++/gtest and&amp;nbsp;Java/Maven.&lt;/p&gt;
&lt;p&gt;This effort started as a Cloudera hackathon project that Todd and I worked on two years ago, and I&#39;m very glad we got it across the line.
Furthermore, it&#39;s also &lt;a href=&quot;https://github.com/cloudera/dist_test&quot;&gt;open-source&lt;/a&gt;, and we&#39;d love to see it rolled out to more&amp;nbsp;projects.&lt;/p&gt;
&lt;p&gt;Slides: &lt;a href=&quot;/presentations/distributed_testing_apache_big_data_2016.pptx&quot;&gt;pptx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Source-code: &lt;a href=&quot;https://github.com/cloudera/dist_test&quot;&gt;cloudera/dist_test&lt;/a&gt;&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;


   </content></entry><entry><title>Windows Azure Storage</title><author><name>Andrew Wang</name></author><link href="http://www.umbrant.com/blog/2016/windows_azure_storage.html"/><updated>2016-02-04T21:23:00Z</updated><published>2016-02-04T21:23:00Z</published><id>http://www.umbrant.com/blog/2016/windows_azure_storage.html</id><content type="html">
       

&lt;!-- Hyde::Excerpt::Begin --&gt;

&lt;p&gt;What makes this paper special is that it is one of the only published papers about a production &lt;em&gt;cloud&lt;/em&gt; blobstore. The 800-pound gorilla in this space is Amazon S3, but I find Windows Azure Storage (WAS) the more interesting system since it provides strong consistency, additional features like append, and serves as the backend for not just WAS Blobs, but also WAS Tables (structured data access) and WAS Queues (message delivery). It also occupies a different design point than hash-partitioned blobstores like Swift and&amp;nbsp;Rados.&lt;/p&gt;
&lt;p&gt;This paper, &quot;&lt;a href=&quot;http://blogs.msdn.com/b/windowsazurestorage/archive/2011/11/20/windows-azure-storage-a-highly-available-cloud-storage-service-with-strong-consistency.aspx&quot;&gt;Windows Azure Storage: A Highly Available Cloud Storage Service with Strong Consistency&lt;/a&gt;&quot; by Calder et al., was published at SOSP&amp;nbsp;&#39;11.&lt;/p&gt;
&lt;!-- Hyde::Excerpt::End --&gt;

&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;Most people are familiar with filesystems. Filesystems have a hierarchical namespace composed of a nested directory tree, where directories contain files. Directories and files can have various bits of metadata attached to them like permissions, modtime, ACLs, xattrs, etc. Directories are used to logically group files together, and there are commands that work on entire recursive directory trees (mv, rm -r, chown -r,&amp;nbsp;etc).&lt;/p&gt;
&lt;p&gt;Blobstores are like filesystems, except simpler. A unifying characteristic of blobstores is that they do not provide a hierarchical namespace. Instead, you get multiple flat namespaces (which S3 calls &lt;em&gt;buckets&lt;/em&gt; and WASB &lt;em&gt;partitions&lt;/em&gt;), in which you can store blobs. Blobstores also provide fewer features than filesystems. It&#39;s typical to not support an operation like rename, setting per-blob permissions, and also have preconditions around IO (e.g. S3 requires a full-blob checksum at upload time, and no random writes or appends) which push complexity to the&amp;nbsp;application-level.&lt;/p&gt;
&lt;p&gt;You might read this and think that blobstores sound terrible, but there&#39;s a very good reason for throwing away these features: horizontal scalability. It&#39;s difficult to shard a hierarchical namespace, and it&#39;s even harder to support operations like directory rename, so blobstores punt on these problems. As a result, you have a system that architecturally has infinite&amp;nbsp;scale.&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;In the datacenter, WAS is composed of &lt;em&gt;stamps&lt;/em&gt;, which are sets of 10-20 racks of servers. This is what others might call a &lt;em&gt;cell&lt;/em&gt; or &lt;em&gt;pod&lt;/em&gt;, it&#39;s used as a unit of deployment and management. There are many stamps per datacenter, and many datacenters which are geographically distributed for&amp;nbsp;fault-tolerance.&lt;/p&gt;
&lt;p&gt;Users have &lt;em&gt;accounts&lt;/em&gt; and all of the data in an account is stored on a single stamp. Accounts are another unit of management, and are migrated between stamps based on&amp;nbsp;load.&lt;/p&gt;
&lt;p&gt;WAS is a very layered system, so let&#39;s take it from the bottom up, starting with how it works within a single stamp, then talking about how multiple stamps are glued together into a global&amp;nbsp;namespace.&lt;/p&gt;
&lt;h3&gt;Stream&amp;nbsp;Layer&lt;/h3&gt;
&lt;p&gt;The bottom-most layer in the stack is the &lt;em&gt;stream layer&lt;/em&gt;. The stream layer exposes a flat namespace of append-only logs called &lt;em&gt;streams&lt;/em&gt;. Streams are composed of &lt;em&gt;extents&lt;/em&gt;, which are a unit of replication and are about &lt;em&gt;1GB&lt;/em&gt; and stored on as files on a local filesystem. Only the last extent in the stream can be appended to. Extents in turn are composed of &lt;em&gt;blocks&lt;/em&gt;, which variable-length up to 4MB in size, and are the unit of a client read or write. Blocks are also the unit of checksumming, so the entire block is read at read time to verify the&amp;nbsp;checksum.&lt;/p&gt;
&lt;p&gt;Architecturally, this looks a lot like HDFS. There is a Paxos-replicated &lt;em&gt;stream master&lt;/em&gt; which maintains a mapping of streams-to-extents and extents-to-nodes. It chooses which nodes to use for incoming writes, routes reads to the correct and re-replicates extents when nodes fail. The stream master needs to keep these mappings in memory, and it&#39;s designed for approximately 100k streams and 50 million total&amp;nbsp;extents.&lt;/p&gt;
&lt;p&gt;Notably, the stream master does not track the extent-to-block mapping, which would not fit on a single machine. Instead, this is handled by the extent nodes, which maintain an index of the block offsets alongside the extent&amp;nbsp;file.&lt;/p&gt;
&lt;p&gt;The stream layer uses chain replication when writing an extent, the same method as HDFS but with some differences I&#39;ll be highlighting. Chain replication is nice since it&#39;s less complicated than Paxos replication and you can get better throughput by pipelining. The master is off the hot-path during data writes; the writer goes directly to the three chosen extent nodes. One of these nodes is the &lt;em&gt;primary&lt;/em&gt; and pushes whole-block writes down the pipeline and acks the client when complete. These block writes are atomic (and there is even an atomic multi-block append). Combined, this has the nice property of allowing concurrent writers to an extent, since the primary orders the incoming block appends and serves all reads to the extent while it&#39;s being written to. HDFS does not have atomic appends, but does allow applications to block for data to be durable (hflush/hsync) which provides similar properties if you use a length-prefixed record format with a checksum, and roll a new file on&amp;nbsp;failure.&lt;/p&gt;
&lt;p&gt;Talking more about failures, in WAS, failures during a write are handled by sealing the extent and starting a new one. Sealing the extent requires agreeing on the length of the extent, which is coordinated with the stream master. The SM asks the remaining nodes for the length of the extent, and uses the smallest length. This is safe since only writes are only ack&#39;d to the client after they are fully-replicated. Longer lengths are also okay, since stream clients are required to handle duplicate blocks in a stream. Once sealed, this is the final length of the extent. If a version of the extent with a different length appears, it is safely&amp;nbsp;discarded.&lt;/p&gt;
&lt;p&gt;HDFS does something more complicated called &lt;em&gt;pipeline recovery&lt;/em&gt; to try and keep writing rather than rolling to a new HDFS block. This is because we want to produce fewer, larger blocks to reduce NameNode memory usage, and for a long time HDFS did not support variable-length blocks (for no really good reason). HDFS pipeline recovery has been described as &quot;an informally-specified implementation of two-phase commit&quot;, and you can read all about it (and other recovery processes) in an excellent &lt;a href=&quot;http://blog.cloudera.com/blog/2015/02/understanding-hdfs-recovery-processes-part-1/&quot;&gt;series&lt;/a&gt; of &lt;a href=&quot;https://blog.cloudera.com/blog/2015/03/understanding-hdfs-recovery-processes-part-2/&quot;&gt;blog posts&lt;/a&gt; written by my colleague Yongjun&amp;nbsp;Zhang.&lt;/p&gt;
&lt;p&gt;The stream layer also implements background erasure coding of sealed extents, as well as latency-levelling by a similar mechanism to Jeff Dean&#39;s &lt;a href=&quot;http://research.google.com/pubs/pub40801.html&quot;&gt;The Tail at Scale&lt;/a&gt; work. They also do some ops tricks to further improve latency, like allocating a separate SSD to buffer writes and doing deadline IO&amp;nbsp;scheduling.&lt;/p&gt;
&lt;h3&gt;Partition&amp;nbsp;Layer&lt;/h3&gt;
&lt;p&gt;Now, we move onto the co-designed user of the stream layer: the &lt;em&gt;partition layer&lt;/em&gt;, which maintains the user-visible constructs like blobs, tables, and&amp;nbsp;queues.&lt;/p&gt;
&lt;p&gt;The partition layer is a range-partitioned distributed database. These ranges can be split and merged and moved around based on&amp;nbsp;load.&lt;/p&gt;
&lt;p&gt;There is a table for each of the three user-visible constructs, a table that describes the schema of these three tables, and finally a table of the mapping of ranges to servers (like the meta table in HBase). The primary key for the three &lt;em&gt;object tables&lt;/em&gt; is a compound key of &lt;em&gt;(account, partition, object name)&lt;/em&gt; (user-level identifiers), and other columns describe what stream, extent, offset, and length have the corresponding data for that object. Since it&#39;s a range-partitioned distributed database that uses LSM-trees under the hood, I&#39;m going to point to &lt;a href=&quot;http://getkudu.io/&quot;&gt;Kudu&lt;/a&gt; and &lt;a href=&quot;https://hbase.apache.org/&quot;&gt;HBase&lt;/a&gt; as similar systems if you want to learn more. The split and merge process looks the same as HBase, and implementation details like the memstore, bloom filters, and row caching are present in all of these&amp;nbsp;systems.&lt;/p&gt;
&lt;p&gt;Each range uses a couple streams to maintain its state. The two important ones are a commit stream (a WAL) and a row data stream (checkpoints of WAL mutations, HFiles in HBase parlance) which are used to maintain the LSM tree. They also implement a BLOB type which writes blob data into a side stream to avoid the write amplification of LSM trees, instead using pointers and efficient stream concat operations to avoid rewriting&amp;nbsp;data.&lt;/p&gt;
&lt;p&gt;One interesting point is that on a per stamp basis, they see 75 splits and merges and 200 partition moves every day. That&#39;s a lot more than I would have guessed for HBase, but since the partition layer worry about storage locality, moving a partition is cheap. An efficient stream concat operation means you can avoid rewriting data when doing&amp;nbsp;merges.&lt;/p&gt;
&lt;h3&gt;Front-end&amp;nbsp;service&lt;/h3&gt;
&lt;p&gt;The front-end service is a proxy for user requests which interacts with WAS on behalf. Front-ends are stateless, perform authentication/authorization, and caches the partition map for faster lookups. This is pretty standard in web&amp;nbsp;services.&lt;/p&gt;
&lt;h3&gt;Location&amp;nbsp;service&lt;/h3&gt;
&lt;p&gt;Gluing together stamps is the &lt;em&gt;location service&lt;/em&gt;, which sits above the partition layer and maintains a global namespace of accounts-to-stamps. We now have the full picture of how to find an object given its (account, partition, name) tuple. The location service tells us which stamp has the account, the partition layer at that stamp translates partition and name to (stream, extent, offset, length), and then the stream layer translates the (extent, offset, length) into a block on disk (the read&amp;nbsp;unit).&lt;/p&gt;
&lt;p&gt;The location service is responsible for moving accounts between stamps for load balancing, and also for &lt;em&gt;inter-stamp replication&lt;/em&gt; for disaster fault-tolerance. An account has a primary stamp and some number of secondaries. The location service links them together, and the partition layer in the primary stamp asynchronously replicates to the secondaries. This async process is pretty fast, they say on average it takes 30s to replicate&amp;nbsp;changes.&lt;/p&gt;
&lt;p&gt;In the case of a disaster, the location service flips DNS and VIP over to point to a secondary cluster. This does mean that something like 30s of data will be lost, since replication is asynchronous. These events do happen too. It&#39;s not just meteor strikes, I&#39;ve heard some funny stories about hunters shooting down power lines or utility crews cutting network&amp;nbsp;cables.&lt;/p&gt;
&lt;h3&gt;Questions and&amp;nbsp;comments&lt;/h3&gt;
&lt;h4&gt;How many hops /&amp;nbsp;IOs?&lt;/h4&gt;
&lt;p&gt;It&#39;s interesting to try and count how many network hops and IOs are required to do operations in this system. Let&#39;s look at a typical read&amp;nbsp;request.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Client has the DNS cached so goes to a front-end at the primary stamp. 1 hop, 0&amp;nbsp;IOs.&lt;/li&gt;
&lt;li&gt;Front-end has the partition map cached, so goes to the partition server for that range. 1 hop, 0&amp;nbsp;IOs.&lt;/li&gt;
&lt;li&gt;Partition server looks in the row data stream for the extent/offset/length of the blob. This is an LSM tree, so requires 0-N lookups to the stream layer (let&#39;s assume 1), and the blob layer is 1 lookup. For each stream lookup:&lt;ol&gt;
&lt;li&gt;Go to the stream master to figure out which extent nodes have the extent. 1 hop, 0&amp;nbsp;IOs.&lt;/li&gt;
&lt;li&gt;Go to the extent node to read the data. This requires looking in the index and then the actual data. 1 hop, ~3 IOs. This depends on caching of file handles and the filesystem, so an&amp;nbsp;estimate.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My math says this is a total of 6 hops and 6 IOs to do a read. How about writing a small&amp;nbsp;blob?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Client has the DNS cached so goes to a front-end at the primary stamp. 1 hop, 0&amp;nbsp;IOs.&lt;/li&gt;
&lt;li&gt;Front-end has the partition map cached, so goes to the partition server for that range. 1 hop, 0&amp;nbsp;IOs.&lt;/li&gt;
&lt;li&gt;Partition server writes to the WAL stream and the side blob stream. For each write:&lt;ol&gt;
&lt;li&gt;Go to the stream master to find the primary extent node for the extent. 1 hop, 0&amp;nbsp;IOs.&lt;/li&gt;
&lt;li&gt;Go to the primary extent node, write the block. This gets pushed to the two other nodes in the pipeline. Appending to an open file is ~2 IOs (one for the data, one to update the file length in the inode). I&#39;ll assume the index is in memory while the extent is unsealed, and checksums are stored inline, else this blows up. 3 hops, 6&amp;nbsp;IOs.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;At this point the write can be&amp;nbsp;ACK&#39;d.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Add it up, this is ten hops and 12 IOs. This is multiplied by the number of stamp replicas, and again by some small factor for LSM rewrites, and by 2 since all writes are&amp;nbsp;journaled.&lt;/p&gt;
&lt;h4&gt;Account&amp;nbsp;limits&lt;/h4&gt;
&lt;p&gt;S3 lets you can store an &lt;a href=&quot;http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html&quot;&gt;unlimited number of items in a bucket&lt;/a&gt; with no performance implications. This is not the case in WAS, which limits accounts to &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/azure-subscription-service-limits/#storage-limits&quot;&gt;500TB usage, 20K IOPS, and 10-30Gbps network throughput&lt;/a&gt;. Remember also that an account can have multiple partitions! This is because accounts are assigned to a stamp, so it&#39;s limited by the capacity of that one&amp;nbsp;stamp.&lt;/p&gt;
&lt;p&gt;Forcing users to partition manually is distinctly worse than the unrestricted buckets provided by S3. Their reasoning was that pinning to a single stamp gives better performance isolation and lets users choose in what geographic region their data is stored. I don&#39;t understand why you couldn&#39;t do the same thing with range-partitioned buckets. With system-managed partitioning, you could get even better stamp utilization by splitting/merging an account across&amp;nbsp;stamps.&lt;/p&gt;
&lt;p&gt;I&#39;m told that Azure Data Lake is the next thing coming down the pipe, is built on top of WAS, and handles sharding across multiple accounts to get around these limits. At this point I begin to wonder; WAS is already a very layered system, and indirections do come at a&amp;nbsp;cost.&lt;/p&gt;
&lt;h4&gt;Centralized&amp;nbsp;control&lt;/h4&gt;
&lt;p&gt;They chose not to use hash-partitioning since they wanted ranged listing and more control over placement for isolation. This is a criticism of hash-partitioning I share. It&#39;s really convenient to split/move partitions around based on load. Range partitioning has the issue of hotspotting writes to sequential keys, but there are tricks like reversing or hashing the key to work around&amp;nbsp;this.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;There are lots of things to like about WAS. They built a number of useful user abstractions on top of a single storage system which provides strong consistency, a fuller feature set than S3, and geo-replication. WAS also has nice provisions to improve latency, increase utilization, and make operations&amp;nbsp;easier.&lt;/p&gt;
&lt;p&gt;I view this as a practical way of stitching together multiple clusters. This is something we support in HDFS via federation, and MapR does something similar with its concept of volumes. It&#39;s also probably true that most users can live just fine with 500TB of storage. I just find it somewhat dissatisfying that WAS rids itself of a hierarchical namespace, but doesn&#39;t exploit that fact to the fullest extent to expose a truly scale-out&amp;nbsp;system.&lt;/p&gt;

   </content></entry></feed>
