Espresso: LinkedIn's Distributed Data Serving Platform (Paper)

This paper, written by the LinkedIn Espresso Team, appeared at the ACM SIGMOD/PODS Conference (June 2013). To see the talk given by Swaroop Jagadish (Staff Software Engineer @ LinkedIn), go here:
http://www.slideshare.net/amywtang/li-espresso-sigmodtalk

1.
On Brewing Fresh Espresso:LinkedIn’s Distributed Data Serving PlatformLin Qiao, Kapil Surlaker, Shirshanka Das, Tom Quiggle, Bob Schulman, Bhaskar Ghosh,Antony Curtis, Oliver Seeliger, Zhen Zhang, Aditya Auradkar, Chris Beavers, Gregory Brandt,Mihir Gandhi, Kishore Gopalakrishna, Wai Ip, Swaroop Jagadish, Shi Lu,Alexander Pachev, Aditya Ramesh, Abraham Sebastian, Rupa Shanbhag,Subbu Subramaniam, Yun Sun, Sajid Topiwala, Cuong Tran, Jemiah Westerman, David ZhangLinkedIn, Inc.Mountain View, CA, USA{lqiao,ksurlaker,sdas,tquiggle,bschulman,bghosh,acurtis,oseeliger,zzhang,aauradkar,cbeavers,gbrandt,mgandhi,kgopalakrishna,wip,sjagadish,slu,apachev,aramesh,asebastian,rshanbhag,ssubramanian,ysun,stopiwal,ctran,jwesterman,dzhang}@linkedin.comABSTRACTEspresso is a document-oriented distributed data servingplatform that has been built to address LinkedIn’s require-ments for a scalable, performant, source-of-truth primarystore. It provides a hierarchical document model, transac-tional support for modiﬁcations to related documents, real-time secondary indexing, on-the-ﬂy schema evolution andprovides a timeline consistent change capture stream. Thispaper describes the motivation and design principles involvedin building Espresso, the data model and capabilities ex-posed to clients, details of the replication and secondaryindexing implementation and presents a set of experimen-tal results that characterize the performance of the systemalong various dimensions.When we set out to build Espresso, we chose to apply bestpractices in industry, already published works in researchand our own internal experience with diﬀerent consistencymodels. Along the way, we built a novel generic distributedcluster management framework, a partition-aware change-capture pipeline and a high-performance inverted index im-plementation.Categories and Subject Descriptors: C.2.4 [DistributedSystems]: Distributed databases; H.2.4 [Database Man-agement]: Systems–concurrency, distributed databasesGeneral Terms: Algorithms, Design, Performance, Relia-bilityKeywords: Large Databases, Transactions, Secondary In-dexing, Cluster Management, Change Data Capture, MySQLPermission to make digital or hard copies of all or part of this work forpersonal or classroom use is granted without fee provided that copies arenot made or distributed for proﬁt or commercial advantage and that copiesbear this notice and the full citation on the ﬁrst page. To copy otherwise, torepublish, to post on servers or to redistribute to lists, requires prior speciﬁcpermission and/or a fee.SIGMOD’13, June 22–27, 2013, New York, New York, USA.Copyright 2013 ACM 978-1-4503-2037-5/13/06 ...$15.00.1. INTRODUCTIONTo meet the needs of online applications, Relational DatabaseManagement Systems (RDBMSs) have been developed anddeployed widely, providing support for data schema, richtransactions, and enterprise scale.In its early days, the LinkedIn data ecosystem was quitesimple. A single RDBMS contained a handful of tables foruser data such as proﬁles, connections, etc. This RDBMSwas augmented with two specialized systems: one providedfull text search of the corpus of user proﬁle data, the otherprovided eﬃcient traversal of the relationship graph. Theselatter two systems were kept up-to-date by Databus [14], achange capture stream that propagates writes to the RDBMSprimary data store, in commit order, to the search and graphclusters.Over the years, as LinkedIn evolved, so did its data needs.LinkedIn now provides a diverse oﬀering of products and ser-vices to over 200 million members worldwide, as well as acomprehensive set of tools for our Talent Solutions and Mar-keting Solutions businesses. The early pattern of a primary,strongly consistent, data store that accepts reads and writes,then generates a change capture stream to fulﬁll nearlineand oﬄine processing requirements, has become a commondesign pattern. Many, if not most, of the primary data re-quirements of LinkedIn do not require the full functionalityof a RDBMS; nor can they justify the associated costs.Using RDBMS technology has some associated pain points.First, the existing RDBMS installation requires costly, spe-cialized hardware and extensive caching to meet scale andlatency requirements. Second, adding capacity requires along planning cycle, and is diﬃcult to do at scale with 100%uptime. Third, product agility introduces a new set of chal-lenges for schemas and their management. Often the datamodels don’t readily map to relational normalized forms andschema changes on the production database incur a lot ofDatabase Administrators (DBAs) time as well as machinetime on large datasets. All of the above add up to a costlysolution both in terms of licensing and hardware costs aswell as human operations costs.In 2009, LinkedIn introduced Voldemort [8] to our dataecosystem. Voldemort is inspired by Dynamo [15] and is

2.
a simple, eventually consistent, key:value store. Voldemortwas initially used for soft-state and derived data sets andis increasingly being used for primary data that does notrequire a timeline consistent [12] change capture stream.In early 2011, we took a step back and identiﬁed severalkey patterns that were emerging from our experience withthe RDBMS and Voldemort stack that deﬁned our require-ments for a primary source of truth system.• Scale and Elasticity: horizontal scale, and ability toadd capacity incrementally, with zero downtime.• Consistency: cross-entity transactions and index con-sistency with base data for query-after-write cases• Integration: ability to consume a timeline consistentchange stream directly from the source-of-truth system• Bulk Operations: ability to load/copy all or part of adatabase from/to other instances, Hadoop, and otherdatacenters, without downtime• Secondary Indexing: keyword search, relational predi-cates• Schema Evolution: forward and backward compatibleschema evolution• Cost to Serve: RAM provisioning proportional to ac-tive data rather than total data sizeTo meet all of these requirements, we designed Espresso, atimeline-consistent document-oriented distributed database.Espresso has been deployed into production for several keyuse-cases.The paper is organized as follows. We present key featuresupport in Section 2. We describe the data model, externalAPI, and bulk operations in Section 3. Architecture andsystem components are presented in Section 4. We elabo-rate system implementation in Section 5 . Lessons learnedfrom our software development and operational experienceare summarized in Section 8. Section 6 describes varioususe-cases in production that use Espresso as the source oftruth data store. We present our experimental evaluationof Espresso in Section 7, describe future work in Section 9,discuss related work in Section 10, and conclude in Section11.2. FEATURESAs we set out to meet the requirements, we chose thesedesign principles as we built Espresso:• Use proven oﬀ-the-shelf open-source components• Learn from the design decisions of similar systems inother companies• Make sure operability is designed in from the beginningIn this paper, we will discuss some hard problems we didhave to solve to meet our requirements. For example, nativeMySQL replication had to be modiﬁed to support our scaleand elasticity requirements, and Lucene for full-text index-ing required modiﬁcations to use at our scale. In the designand building of Espresso, these key capabilities required usto engineer new solutions to meet our set of requirements.In addition to being a scalable and elastic distributed systemwith low cost to serve, the following is a list of key featuresEspresso provide.Transaction Support. Most NoSQL stores do not pro-vide transaction support beyond a single record/document.A large number of use-cases at Linkedin lend themselvesto a natural partitioning of the data into collections thatshare a common partitioning key e.g. memberid, compa-nyid, or groupid. Applications often require that relatedentities be updated atomically. To model these use-cases,Espresso supports a hierarchical data model and providestransaction support on related entities.Consistency Model. Brewer’s CAP theorem [10] statesthat a distributed system can only achieve 2 out of consis-tency, availability and partition-failure tolerance. Espressois built to be a source-of-truth system for primary user dataat Linkedin and needs to maintain consistency. As we de-scribe later in this paper, Espresso follows the master-slavemodel, where read and write requests for each partition areserved by the node acting as the master, while the slavesreplicate from the masters to provide durability in case ofmaster failure. However, using synchronous replication be-tween replicas comes at the cost of high latency. SinceEspresso supports online user requests, maintaining low la-tency for operations is critical. For this reason, we relax theconsistency to be timeline consistent, where the replicationbetween master and slave is either asynchronous or semi-synchronous, depending on the application requirements. Inthe event of a master failure, the cluster manager promotesone of the slave replicas to be the new master and thus thesystem maintains availability.Integration with the complete data ecosystem. Pro-viding a data plaform as a service for application developersis a big motivation for building Espresso. An equally impor-tant goal is to support developer agility by ensuring tightintegration with rest of the data ecosystem at Linkedin. Alarge number of specialized online systems such as graph andsearch indexes rely on getting a low-latency stream from theprimary system. In addition, our applications depend on of-ﬂine data analysis in Hadoop. Providing out-of-the-box ac-cess to the change stream is an afterthought in many otherNoSQL solutions. In Espresso, we have made this feature aﬁrst-class citizen. This ensures that processors in a nearlineor oﬄine environment can be added independently, with-out making any change to the source system. Results fromnearline or oﬄine computation often need to be served backto users and supporting these ﬂows natively is also a keycapability of Espresso.Schema Awareness and Rich Functionality. Un-like some other NoSQL stores [2, 5] that are schema-free,Espresso supports schema deﬁnition for the documents. En-forcing schemas allows systems across the entire data ecosys-tem to reason about the data in a consistent way and alsoenables key features such as secondary indexing and search,partial updates to documents and projections of ﬁelds withindocuments.Espresso avoids the rigidity of RDBMSs by allowing on-the-ﬂy schema evolution. It is possible to add a new ﬁeldto a document at any time and this change is backward-compatible across the entire ecosystem. When such a changeis made, producers as well as downstream consumers of thedata do not have to change simultaneously. In an environ-ment where rapid change and innovation is required, theability to make changes in production without complex pro-cess and restrictions is essential.

3.
3. EXTERNAL INTERFACE3.1 Data ModelEspresso’s data model emerged out of our observationsof typical use-cases and access patterns at LinkedIn. Wewanted to provide something richer than a pure key-valuedata model, while not forcing ourselves into non-scalablepatterns. To that end, we recognized the presence of twoprimary forms of relationships that exist commonly in oureco-system:• Nested Entities: We often ﬁnd a group of entities thatlogically share a common nesting hierarchy. e.g. Allmessages that belong to a mailbox and any statisticsassociated with the mailbox, or all comments that be-long to a discussion and the meta-data associated withthe discussion. The primary write pattern to theseentity groups involve creating new entities and/or up-dating existing entities. Since the entities are logi-cally related, mutations often happen in a group, andatomicity guarantees here are very helpful in simplify-ing the application logic. The read patterns are typ-ically unique-key based lookups of the entities, ﬁlter-ing queries on a collection of like entities or consistentreads of related entities. For example, show me all themessages in the mailbox that are marked with the ﬂagisInbox and sort them based on the createdOn ﬁeld.• Independent Entities: Complementary to the NestedEntities model, we also ﬁnd independent entities whichhave many:many relationships. e.g. People and Jobs.The primary write pattern to these tend to be indepen-dent inserts/updates. Applications tend to be moreforgiving of atomicity constraints around updates totop-level Entities, but do need guarantees that anyupdates that apply to both Entities must eventuallyhappen. When a Person applies for a Job, the per-son must see the eﬀect of that write right away; inher jobs dashboard, she must see that she has appliedfor the job. The Job poster who is monitoring all jobapplications for the job must eventually see that theperson has applied for the job, but the update may bedelayed.Espresso uses a hierarchical data model to model NestedEntities eﬃciently. Independent Entities with relationshipsare modeled as disjoint Entities with the change capturestream available for materializing both sides of the rela-tionship. Semantically, the data hierarchy is composed ofDatabases, Document Groups, Tables and ﬁnally Documents.We describe these terms in more detail below:Document. A Document is the smallest unit of data rep-resented in Espresso. Documents logically map to entities,are schema-ed and can have nested data-structures such aslists, arrays and maps as ﬁelds within the document. In SQLterms, documents are like rows in a table. They are iden-tiﬁed by a primary key which can be composed of multiplekey parts. Document schemas allow annotations on ﬁelds forsupporting indexed access. Secondary indexing is covered indepth in Section 5.Table. An Espresso Table is a collection of like-schema-ed documents. This is completely analogous to a table in arelational database. A table deﬁnes the key structure thatis used for uniquely identifying documents that it stores. Inaddition to externally deﬁned keys, Espresso also supportsauto-generation of keys.Document Group. Document Groups are a logical con-cept and represent a collection of documents that live withinthe same database and share a common partitioning key.Readers familiar with MegaStore [9] will recognize the sim-ilarity to the Entity Groups concept in the MegaStore datamodel. Document Groups are not explicitly represented, butinferred by the mere act of sharing the partitioning key. Doc-ument Groups span across tables and form the largest unitof transactionality that is currently supported by Espresso.For example, one could insert a new document in the Mes-sages table as well as update a document in the MailboxStatstable atomically, as long as both the documents are part ofthe same document group (keyed by the same mailboxId).Database. Espresso Databases are the largest unit ofdata management. They are analogous to databases in anyRDMBS in that they contain tables within them. All docu-ments within a Database are partitioned using the same par-titioning strategy. The partitioning strategy deﬁnes the datapartitioning method, e.g. hash partitioning or range parti-tioning, and other details such as the total number of par-titions. Databases are physically represented by a Databaseschema that encodes the required information such as thepartitioning function, number of buckets etc.3.2 APIEspresso oﬀers a REST API for ease of integration. In thissection, we will focus less on the details of the API and moreon the capabilities provided to the application developer.3.2.1 Read OperationsEspresso provides document lookup via keys or secondaryindexes. There are three ways to lookup via keys: 1) spec-ify a complete primary key and a single document gets re-turned, 2) specify a list of resource keys sharing the sameleading key, and a list of documents get returned, and 3)specify a projection of ﬁelds of a document, and only therequired ﬁelds are returned. Local secondary index queriesare performed by providing the partition key that identiﬁesthe document group, the table for which the query needs tobe run and the attributes or search terms in the query.3.2.2 Write OperationsEspresso supports insertion or full update of a single doc-ument via a complete key. Advanced operations that aresupported include 1) partial update to a document givena complete key, 2) auto-increment of a partial key, and 3)transactional update to document groups (across multipletables but sharing the same partitioning key). Examplesof partial updates include increment/decrement operationson “number” ﬁelds, a typical requirement for implementingcounters. Deletes are supported on the full keys as well aspartial keys.3.2.3 ConditionalsConditionals are supported on both Reads and Writes andcurrently support simple predicates on time-last-modiﬁedand etag (CRC of the document contents). These are typi-cally used to implement the equivalent of compare-and-swapstyle operations. In practice, since the rich API allows fairlycomplex server-side processing, we rarely see conditionalwrites.

4.
3.2.4 Multi OperationsAll read and write operations have their “Multi” coun-terparts to support multiple operations grouped into onetransaction. These are typically used for batch operations.3.2.5 Change Stream ListenerEspresso also provides a Change Stream Listener APIthrough Databus. This allows an external observer to ob-serve all mutations happening on the database while preserv-ing the commit-order of the mutations within a documentgroup. The API allows an observer to consume the stream,while noticing transaction boundaries to maintain consis-tency at all times if required. Each individual change recordcontains the type of the change (Insert, Update, Delete),the key of the document modiﬁed, the pre and post-image(if applicable) and the SCN of the change. This allows veryeasy integration with nearline processors that are perform-ing streaming computations and other kinds of processingon the change stream.3.3 Bulk Load and ExportAn important part of Espresso’s place in the data ecosys-tem is its seamless integration with the end-to-end data ﬂowcycle. To that end, we support eﬃcient ingestion of largeamounts of data from oﬄine environments like Hadoop intoEspresso. Hadoop jobs emit to a specialized output formatthat writes out the primary data as well as the index up-dates (if applicable) and applies the partitioning functionto write out ﬁles that align with the online partitions. Asa ﬁnal step, the output format notiﬁes the online Espressocluster about the new data and its location by interactingwith Helix. Each storage node then pulls in the changesthat are related to the partitions that it is hosting and ap-plies them locally using eﬃcient bulk-insert paths. The bulkingest operation supports incremental semantics and can goon while reads and writes are happening to the online dataset. Storage nodes indicate completion after processing theirindividual tasks allowing an external observer to monitor theprogress of the bulk import very easily.The inverse operation to bulk data ingest is data export.This is extremely important to support ETL and other kindsof large oﬄine data analysis use-cases. We use Databus toprovide the real-time stream of updates which we persistin HDFS. Periodic jobs additionally compact these incre-mental updates to provide snapshots for downstream con-sumption. This allows oﬄine processors to either processthe change activity or the entire data set depending on theirneeds. There are a host of interesting sub-problems herelike metadata management and evolution, inexact alignmentbetween wall-clock time and the logical clock timeline pro-vided by Espresso’s commit log, and ensuring end-to-enddata integrity and consistency. For brevity, we skip thesediscussions in this paper.4. SYSTEM ARCHITECTURE4.1 System OverviewThe Espresso system consists of four major components:clients and routers, storage nodes, databus relays and clus-ter managers. The overall system architecture is depictedin Figure 1, where the data ﬂow is depicted in solid arrows,The data replication ﬂow is depicted in double solid arrows,and the cluster state control ﬂow is shown in dotted line.Espresso clients generate reads and writes to routers, androuters pass the requests to storage nodes who own the data.Storage node process the request through the primary keyor the secondary index lookup. Changes to the base datais replicated from one storage node to databus relays, andconsumed by another storage node which maintains a con-sistent replica of the primary source. Cluster manager, akaHelix, monitors and manages the state of the entire cluster,including storage nodes, and databus relays. Routers andstorage nodes are also spectators of the states the cluster,so they react to cluster state changes accordingly.RouterClientRouterStorage NodeBaseDataIndex......Databus RelayDatabus RelayDatabus RelayStorage NodeBaseDataIndexStorage NodeBaseDataIndexHelixHelixHelixDownstreamConsumerDownstreamConsumerDownstreamConsumer......Data Flow Replication Flow Cluster Control FlowFigure 1: Espresso Architecture in a Single Colo4.2 System ComponentsClient and Router. An application sends a request toan Espresso endpoint by sending an HTTP request to arouter, which inspects the URI, forwards the request to theappropriate storage node(s), and assembles a response. Therouting logic uses the partitioning method for the databaseas speciﬁed in the database schema and applies the appro-priate partitioning function, e.g. a hash function, to thepartition key of the URI to determine the partition. Then ituses the routing table that maps each partition to the mas-ter storage node, and sends the request to that node. For arequest without a partitioning key, such as an index searchquery on the whole data set, the router queries all storagenodes, and sends the merged result set back to the client.Cluster Manager. Espresso’s cluster manager uses ApacheHelix [16]. Given a cluster state model and system con-straints as input, it computes an ideal state of resource dis-tribution, monitors the cluster health, and redistributes re-sources upon node failure. Helix throttles cluster transitionsduring resource redistribution, and ensures the cluster is al-ways in a valid state, whether in steady-state mode or whileexecuting transitions.Each Espresso database is horizontally sharded into a num-ber of partitions as speciﬁed in the database schema, witheach partition having a conﬁgurable number of replicas. Foreach partition, one replica is designated as master and therest as slaves. Helix assigns partitions to storage nodes in

5.
accordance with these constraints: 1) only one master perpartition (for consistency), 2) master and slave partitions areassigned evenly across all storage nodes (to balance load),3) no two replicas of the same partition may be located onthe same node or rack (for fault-tolerance), and 4) mini-mize/throttle partition migration during cluster expansion(to control impact on the serving nodes).Helix [16] is a generic cluster management system, andan Apache incubator project. Please refer to the Helix pa-per [16] for an in-depth description.Storage Node. The storage node is the building blockfor horizontal scale. Data is partitioned and stored on stor-age nodes, which maintain base data and corresponding localsecondary indexes. Storage nodes also provide local transac-tion support across entity groups with a common root key.To achieve read-after-write consistency, storage nodes applyupdates transactionally to base tables and their secondaryindexes.Storage nodes maintain replicas using a change log streamprovided by the replication tier. Committed changes to basedata are stored locally in a transaction log that is pulledinto the replication tier. Slave partitions are updated byconsuming change logs from the replication tier, and apply-ing them transactionally to both the base data and the localsecondary index.In addition to serving requests from the client and fromthe replication tier, a storage node also runs utility tasksperiodically, including consistency checking between masterand slave partitions, and backups. To minimize performanceimpact on read/write requests, utility tasks are throttled tocontrol resource consumption.Databus. For replication, Espresso uses Databus [14],LinkedIn’s change data capture pipeline to ship transac-tion events in commit order and provide Timeline Con-sistency. Timeline Consistency is a form of eventual con-sistency, which guarantees that events are applied in thesame order on all replicas for each partition. This featureprovides the basis for implementing failover and rebalanc-ing. Changes from master partitions are captured, trans-ferred and applied to the replicas in the following manner.First, on a storage node, all changes are tagged with a localtransaction sequence number and logged. Second, the log isshipped to Databus relay nodes. Third, a diﬀerent storagenode pulls changes from Databus relay and applies them toits slave partitions. All these steps follow the transactioncommit order so that timeline consistency is guaranteed.Databus achieves very low replication latency, and highthroughput. It can easily scale to thousands of consumers.It provides data ﬁltering, allowing a consumer to subscribeto changes from a speciﬁed subset of tables in a sourcedatabase. A databus consumer can also selectively chooseevents of interest at various granularities. For example, aconsumer can subscribe to a list of partitions or a set oftables. The same Databus instances used for replicationprovide an external change capture pipeline for downstreamconsumers within the data ecosystem, such as Hadoop clus-ters, the social graph engine, people search service, etc.5. IMPLEMENTATION5.1 Secondary IndexIn contrast to a simple key-value data model, the Docu-ment Groups data model allows us to support certain formsof secondary indexing very eﬃciently. One simple use-casefor this is selecting a set of documents from a documentgroup based on matching certain predicates on the ﬁelds ofthe documents. In a key-value model, the application de-veloper either has to fetch out all the rows and perform theﬁltering in application code, or has to maintain the primaryrelationship and reverse-mappings for every secondary keythat could be used to access this document. The ﬁrst ap-proach doesn’t scale for very large document groups, thesecond creates the potential for divergence between the pri-mary and reverse-mappings due to the combination of dual-writes in the face of diﬀerent failure scenarios. Addition-ally, if the query involves consulting several such secondarykey-value pairs, the read performance of the system getsimpacted. Thus even though individual key-value lookupsmight be very cheap, the overall cost of a secondary-keybased lookup might be quite high. Secondary Indexes onDocument Groups are what we call local secondary indexes.This is because Espresso stores all entities that belong in thesame entity group on a single node. Secondary Indexes onIndependent-Entity relationships are what we call global sec-ondary indexes. These are typically implemented as derivedtables that are guaranteed to be updated asynchronously byprocessing the change stream emanating from the primaryentity database. In this section, we focus on Espresso’s im-plementation of local secondary indexes.The key functional requirements were:1. Real-Time indexes: Almost all our use-cases with hi-erarchical data models require read your own writesconsistency regardless of whether the access is by pri-mary key or by query on the collection.2. Ease of Schema Evolution: Given the product agility,we see a lot of changes in the schema of these doc-uments. Therefore being able to add new ﬁelds andadd indexing requirements on them with zero down-time and operational ease was a big requirement.3. Query ﬂexibility: Another consequence of product agilityis that queries evolve quickly over time. The tra-ditional wisdom of specifying your queries up front,spending time with the DBA tuning the query to usethe appropriate indexed paths is no longer viable inthis fast-paced ecosystem.4. Text search: Apart from queries on attributes, it isfairly common to allow the user to perform free-formtext search on the document group.We require users to explicitly indicate indexing require-ments on the ﬁelds by annotating them in the Documentschema. Schema evolution rules allow users to add new ﬁeldsand declare them indexed, or index existing ﬁelds.Our ﬁrst attempt used Apache LuceneT M, which is a high-performance, full-featured text search engine library writtenentirely in Java. This was primarily motivated by our re-quirements for text search as well as our in-house expertisewith Lucene. Internally, Lucene uses inverted indexes, whichfulﬁll requirements 2, 3, and 4. However, Lucene has a fewdrawbacks:• It was not initially designed for real-time indexing re-quirements.• The entire index needs to be memory-resident to sup-port low latency query response times.• Updates to the document require deleting the old doc-ument and re-indexing the new one. This is because

6.
Lucene is log-structured and any ﬁles it creates becomeimmutable.We applied several techniques to address the ﬁrst twodrawbacks and bring latencies and memory footprint downto acceptable levels. The ﬁrst idea was to organize the in-dex granularity be per collection key rather than per par-tition. This creates a lot of small indexes but allows ourmemory footprint to be bounded by the working set. Wethen store these millions of small indexes in a MySQL tableto ameliorate the impact of so many small ﬁles and achievetransactionality of the index with the primary store.In our second attempt we built an indexing solution thatwe call the Preﬁx Index. We keep the fundamental buildingblock the same: an inverted index. However, we preﬁx eachterm in the index with the collection key for the documents.This blows up the number of document lists in the invertedindex, but at query time, allows us to only look at the docu-ment lists for the collection that is being queried. The termsare stored on a B+-tree structure thus providing locality ofaccess for queries on the same collection. This is equivalentto having a separate inverted index per collection in termsof memory footprint and working set behavior without theoverhead of opening and closing indexes all the time. Inaddition, we support updates natively to the index becauseour lists are not immutable. Only the lists aﬀected by theupdate are touched during the update operation. Similarto the Lucene implementation, we store the Preﬁx Indexlists in MySQL (InnoDB) to additionally get the beneﬁt oftransactionality with the primary store. During query pro-cessing, the lists that match the query terms are consulted,bitmap indexes are constructed on the ﬂy to speed up theintersecting of these lists and the results are then looked upfrom the primary store to return back to the client.5.2 Partitions and replicasEspresso partitions data to serve two purposes, 1) loadbalancing during request processing time, and 2) eﬃcientand predictable cluster expansion. Since Espresso serveslive online user traﬃc, maintaining the SLA during clus-ter expansion is mission-critical. Cluster expansion throughpartition splitting tends to create signiﬁcant load on the ex-isting serving nodes. Instead, we partition the data into alarge number of partitions to begin with and only migratepartitions to new nodes when cluster expansion is needed.Overpartitioning also keeps the partition size small, whichhas a number of advantages. Ongoing maintenance opera-tions such as backup/restore can be done in less time us-ing parallelism, when partitions are smaller. We have madesigniﬁcant optimizations in our cluster manager (Helix) tomanage the overhead resulting from a large number of par-titions.Each partition is mastered at one node and replicated onn nodes in a traditional master-slave model. We ensure thatslave partitions do not collocate on the same node or rack.Partitions and replicas are completely invisible to externalclients and downstream consumers.5.3 Internal Clock and the TimelineAs described earlier, Espresso partitions its data set usinga partitioning function that can be customized per database.Each database partition operates as an independent commitlog. Each commit log acts as a timeline of data changeevents that occured on that partition forming an internalM1 M2 M3 M4 M5 M6 M7 M8 M9S4 S5 S6 S1 S2 S3 S4 S5 S6S7 S8 S9 S7 S8 S9 S1 S2 S3MySQLReplicationMySQLReplicationMySQLReplicationDatabusReplicationDatabusReplicationMySQL slaveDatabus RelaysStorage Node Storage NodeStorage NodeM1 M2 M3M4 M5 M6M7 M8 M9Figure 2: Partitions and Replication FlowN1N2N3(1,1)(2,1)(3,1)T1T0 T2 T3 T4Figure 3: Timeline of Eventsclock. These timelines are preserved in Databus relays,all Espresso replicas, and provided to all down-stream con-sumers. A timeline consists of an ordered sets of changesin a partition. Each change set is annotated with a mono-tonically increasing system change number (SCN). SCN hastwo parts, generation number and sequence number. Eventscommitted within one transaction all share the same SCN.For each new transaction, the sequence number incrementsby one. For each mastership transfer, the generation numberincrements by one. Figure 3 depicts a timeline of a partitionamong all replicas N1, N2, and N3. At T0, N2 is the master.It sends events starting from SCN (1,1) to other replicas. AtT1, N2 fails. N1 becomes the new master, and starts a newgeneration of SCN (2,1). At T2, N2 comes back and receivesevents generated by N1. At T3, N1 fails, N3 becomes thenew master, and generates events from SCN (3,1). At T4,N1 comes back, and receives all missing events. The timelinefrom all replicas of a given partition is the same.5.4 Replication and ConsistencySeveral large web companies [3, 4, 7] have used shardedMySQL with replicas to build a scalable primary fault-tolerantdata store. There are two ﬂavors of sharded MySQL archi-tectures, the ﬁrst involves one logical partition per MySQLinstance and the second involves multiple logical partitionsper MySQL instance. The ﬁrst approach requires re-shardingduring cluster expansion which is a very painful process asdescribed by the F1 paper [18]. Applying logical shardingon top of MySQL allows the data to be located indepen-dently on disk, which simpliﬁes partition-level backups, re-

7.
stores etc. However, there are still several issues due toMySQL replication that we discovered, 1) MySQL uses logshipping to maintain consistency among replicas, but its logis not partitioned although data is sharded; 2) MySQL doesnot support replication of partial logs. Because of these lim-itations, vanilla MySQL replication cannot achieve the fol-lowing goals, a) load balancing: mixing master and slavepartitions within one MySQL instance so loads are evenlyspread across instances, and 2) online cluster expansion:moving a subset of partitions from one instance to a newinstance without quiescing the cluster. We designed ourreplication layer to address these problems, while still usingMySQL replication stack as a building block.Espresso replication ﬂow is shown in Figure 2. First,we enhanced MySQL’s binary log by adding a new ﬁeld torecord the partition-speciﬁc commit SCN. Using the MySQLreplication protocol, the binary log is pulled into the Databusrelays. Between the MySQL master and the relays, wecan use either semi-synchronous or asynchronous replica-tion. These two options provide a tradeoﬀ between writelatency and durability. When semi-sync replication is used,the commit blocks until the change is acknowledged by atleast one relay. When a slave is promoted to master, it ﬁrstdrains all the events from Databus, thus guaranteeing thatthe slave applies all changes that were committed on themaster.To ensure consistency among replicas, we developed a con-sistency checker. It calculates the checksum of certain num-ber of rows of a master partition, replicates the checksum tostorage nodes running slave partitions. A storage node cal-culates checksum against the same set of rows, and comparesagainst the master checksum. On detection of errors, we ap-ply recovery mechanisms such as restoring a slave partitionfrom a backup of the master partition, to ﬁx the inconsistentslaves.5.5 Fault-toleranceEspresso is designed to be resilient to hardware or softwarefailures. Each system component in Espresso, as describedin Section 4, is fault-tolerant. We elaborate failure handlingfor each component below.As we mentioned before, data is stored in storage nodesin units of partitions. For each partition has replicas, one asa master, and the rest as slaves. When a storage node fails,all the master partitions on that node have to be failed over,meaning for each master partition on the failed node, a slavepartition on a healthy node is selected to take over. The fail-over process is the following. Helix ﬁrst calculate a new setof master partitions from existing slaves, so that the load isevenly spread across the remaining storage nodes. Assume aselected slave partition is at SCN (g, s). The slave partitiondrains any outstanding change events from databus and thentransitions into a master partition. The new master startsa new generation of SCN (g+1, 1).To detect storage node failures, Helix uses two approachesin combination: 1) Use Zookeeper heartbeat for hard failure.If a node fails to send heartbeat for conﬁgurable amount oftime, it is treated as failure; 2) Monitor performance metricsreported by router or storage nodes. When a router or astorage node starts seeing problems, such as a large volumeof very slow queries – an indication of a unhealthy node, itreports to Helix and Helix treats this as failure and initiatesmastership transfer.During the fail-over time, there is transient unavailabilityfor the partitions mastered on the failed node. To minimizetransition latency, Helix always promotes a slave partitionwhich is closest in the timeline to the failed master to becomethe new master. Router can optionally enable slave reads toeliminate read unavailability due to failures. After slave tomaster transition ﬁnishes for a partition, Helix changes therouting table stored on ZooKeeper, so that the router candirect the requests accordingly.Databus is also fault-tolerant. Each databus relay in-stance has n replicas (we use 2 or 3). For every storage node,one relay is designated to be the leader while n-1 are desig-nated to be followers. The leader relay connects to the datasource to pull the change stream while the follower relayspull the change stream from the leader. The clients, includ-ing both espresso storage nodes and external consumers, canconnect to any of the relays, either leader or follower. If theleader relay fails, one of the surviving followers is elected tobe the new leader. The new leader connects to the storagenode and continues pulling the change stream from the lastsequence number it has. The followers disconnect from thefailed leader and connect to the new leader. This deploy-ment drastically reduces the load on the data source serverbut when the leader fails, there is a small delay while a newleader is elected. During this window, the latest changes inthe change stream from a storage node are not available toanother storage node, consuming these changes.Helix is managed by itself with several replicas using Leader-Standby state model. Each helix instance is stateless. Ifcurrent leader fails, a standby instance will be elected to bea leader, and all helix clients, including storage nodes, relaysand routers, will connect to the new leader.5.6 Cluster ExpansionEspresso is elastic: new storage nodes are added as thedata size or the request rate approaches the capacity limitof a cluster. Espresso supports online cluster expansion,which is a business requirement for being an online datastore. When nodes are added we migrate partitions from ex-isting nodes to new ones without pausing live traﬃc. Whenexpanding an Espresso cluster, certain master and slave par-titions are selected to migrate to the new nodes. Helix willcalculate the smallest set of partitions to migrate to mini-mize data movement and cluster expansion time. This waythe cluster expansion time is proportional to the percentageof nodes added. In the future, we plan to enhance Helixto take machine resource capacity into account with hetero-geneous machines accumulated over years, when calculatingthe new partition assignment. For each partition to be mi-grated, we ﬁrst bootstrap this partition from the most recentconsistent snapshot taken from the master partition. Thesepartitions can then become slaves. These slaves consumechanges from databus relay to catch up from current mas-ters.5.7 Multi DatacenterAt present, LinkedIn serves the majority of its traﬃc froma single master data center. A warm standby is located ina geographically remote location to assume responsibility inthe event of a disaster. The Espresso clusters in the disasterrecovery (DR) data center are kept up to date using thechange log stream from the primary clusters. These DRclusters can serve read traﬃc when there are no freshness

8.
requirements but do not serve any write traﬃc. In the eventof a switch-over to the DR site, operators must manually setthe DR cluster to be a primary and enable replication to theold primary.6. ESPRESSO IN PRODUCTIONEspresso has been deployed in production at LinkedInsince June 2012. In this section, we talk about a few use-cases that are running on Espresso today.6.1 Company PagesLinkedIn Company Pages allow companies to create apresence on the LinkedIn network to highlight their com-pany, promote its products and services, engage with follow-ers and share career opportunities. Over 2.6 million compa-nies have created LinkedIn Company Pages. A company’sproﬁle, products and services and recommendations of thoseproducts and services are stored in the BizProﬁle database.Company Pages is an example of a normalized relationalmodel migrated to Espresso. Each Company Page providesproﬁle information about the company. A Company Proﬁlepage may list one or more of the company’s products andservices. Finally members may provide recommendationsfor products and services. This hierarchy is implemented asthree separate tables with products nested under companies,and recommendations nested under products.This use case exhibits a typical read-heavy access patternwith an 1000:1 ratio of reads to writes.6.2 MailboxDBLinkedIn provides InMail, a mechanism for members tocommunicate with one another. A member’s inbox containsmessages sent by other members, as well as invitations toconnect. The MailboxDB is currently migrating from anapplication-sharded RDBMS implementation to Espresso.This use case illustrates several features of Espresso includ-ing collection resources, transactional updates, partial up-date and local secondary indexing.The MailboxDB contains two tables. The Message tablecontains a collection of messages for each mailbox. Eachdocument in the Message table contains subject and bodyof the message along with message metadata such as sender,contining folder, read/unread status, etc.The most frequent request to the InMail service is to get asummary of the inbox content that includes two counts: thenumber of unread messages and the number of pending in-vites. Particularly for large mailboxes, the cost of computingthese counts on each summary request, even by consultingan index, would be prohibitive. Instead, the application ac-tively maintains a separate summary document per mailbox.These summary documents are stored in the Mailbox table.To prevent the counters in the summary document fromgetting out of sync with the data, the update of the Messagetable and the Mailbox table need to be transactional.The MailboxDB has an atypically high write ratio. Inaddition to new mail delivery, each time a member reads anInMail message a write is generated to mark the message asno longer unread. As a result, we see approximately a 3:1read to write ratio for the MailboxDB.6.3 USCPThe Uniﬁed Social Content Platform (USCP) is a sharedplatform that aggregates social activity across LinkedIn. Byintegrating with USCP, a LinkedIn service can annotateits data with social gestures including likes, comments andshares. Services that utilize USCP include LinkedIn Today,the Network Update Stream on a member’s home page andour mobile applications for tablets and phones.The typical USCP access pattern is as as follows. A ser-vice selects the set of domain-speciﬁc data items to displayto a user, then queries the USCP platform for any social ges-tures that have accrued to the selected items. The contentis annotated with the social gestures, if any before it is dis-played. The UI includes calls to action to allow the viewerto like, comment or share. For example, a LinkedIn Todayarticle summary is displayed with a count of the article’sLikes and Comments along with the most recent of each.The USCP workload has a very high Read:Write ratio,and read requests are predominantly multi-GET requests.7. EXPERIMENTAL EVALUATIONWe ran a comprehensive set of experiments to evaluatevarious aspects of our system, including availability underfailure cases, elasticity with cluster expansion, and perfor-mance under diﬀerent workloads and failures.7.1 Test SetupType CPU RAM StorageS1 2x6-core Xeon@2.67GHz 48GB 1.4TB SSD1TB SASS2 2x6-core Xeon@2.67GHz 24GB 1TB SATAComponent Servers Machine TypeStorage Node 12 S1Databus Relay 3 S1Router 3 S2Helix 3 S2ZooKeeper 3 S1Table 1: Machine Conﬁguration and Cluster SetupWe set up a testing cluster in a production fabric as shownin Table 1. We used the production releases of Espresso Stor-age Node, Databus Relay, Helix, and Router. We used theworkloads extracted from USCP and MailboxDB use cases,and enhanced the workloads to evaluate system performancefrom various aspects. The cluster is set up in the followingway. Data on storage nodes is conﬁgured to have 3 repli-cas, one master and two slaves. Databus relay is conﬁguredto have 2 replicas, one leader and one stand-by. Helix isconﬁgured to have one leader node and two stand-by nodes.Finally, Zookeeper uses 3 nodes with 2-node quorum setup.7.2 AvailabilityWe ran a set of experiments to measure time to fail-overwhen a storage node fails. Since our basic fault-toleranceunit is a partition, there is per-partition management over-head. We have varied the total number of partitions, andobserved how it plays a role in availability.First, we increased the total number of partitions from64 to 2048, and measured the fail-over latency when a sin-gle node fails. We compared two conﬁgurations of state-transitions, one with singular-commit and one with group-commit. Singular-commit invokes state transition messagefor each partition, and group-commit invokes state tran-sition for a group of partitions on a storage node. With

9.
!"#!!"$!!!"$#!!"%!!!"&%" (" $%)" %#" #$%" $!%(" %!()"!"#$%&()*+",(-./*01#$$#2(.3*4&,"$*5617()*&8*9")::&-2**+,-."/,0012" 3145-67+"/,0012"Figure 4: Fail-over Latencygroup-commit the amount of disk I/O on ZooKeeper is sig-niﬁcantly reduced. The results are shown in Figure 7.2. Fail-over latency with group commit is signiﬁcantly smaller com-pared with singular commit when the number of partitionsincreases. However, the overall fail-over latency increasestoo. So practically, we cannot use a very large number ofpartitions. We found the knee of the curve to be between512 and 1024 partitions, and is the range recommended forproduction.!"#!!"$!!"%!!"&!!"!!!"#!!"$!!"%$"()*+",-(*.--/"%$"()*+"01)*2.2"34056"7#"()*+",-(*.--/"7#"()*+"01)*2.2"34056"#!$&"()*+",-(*.--/"#!$&"()*+"01)*2.2"34056"!"#$%&()*+",(-./*01#$$#2(.3*889":);<=/>.*" %?9":);<=/>.*" !!9":);<=/>.*"Figure 5: Espresso vs MySQL Fail-over ComparisonSecond, we compared sharded MySQL with Espresso. Weused the same partitioning strategy between Sharded MySQLand Espresso. However, the partition allocation is diﬀerentbetween these two systems. We changed the Helix partitionallocation and fail-over logic to make Espresso behave likeSharded MySQL. For Sharded MySQL, master partitionsand slave partitions cannot be mixed on a single node (ormore precisely a single MySQL instance). Partitions on anode are all master partitions or all slave partitions. Whena master node fails, in Sharded MySQL, a slave node needsto completely take over all partitions, then become a masternode. The fail-over latency for 64, 512, and 2048 partitionsis shown in Figure 5. Espresso always outperforms ShardedMySQL, the higher the number of partitions, the bigger thegap. The reason is during fail-over, Helix evenly distributesfailed partitions to the rest of the cluster, so each runningnode does less work and the degree of fail-over parallelism ishigher. Note that we also plot the fail-over progress in Fig-ure 5. For Espresso, it is making gradual progress to fail-overpartition by partition to healthy storage nodes. For ShardedMySQL, it is all or nothing, so writes to the failed partitionsis blocked until all failed partitions are taken over by a slavenode.7.3 ElasticityAs Espresso supports larger data sizes and request rates,on-line cluster expansion without down-time becomes a keyrequirement. It also needs to be done in a timely fashionwhen expansion is required. When a cluster expands, parti-tions will be moved from existing nodes to new nodes. Theideal amount of data movement should be proportional tothe degree of cluster expansion. We varied the degree of clus-ter expansion from a 6-node cluster and showed the resultsin Figure 6. First, we executed the cluster expansion seri-ally, one partition at a time. The cluster expansion latencymatches very well with the expected % of data moved, whichis derived from the expected % of cluster expanded. Sec-ond, we expanded the cluster in parallel on multiple storagenodes simultaneously. With more nodes added, the higherthe degree of parallelism, the faster the expansion.!"#!"$!"%!"&!"!"(!"!"!"#!!"#!"$!!"$!"%!!"#" $" %" ("!"#$"%&()*+",-./0(1#0"2/)*034"5610)*(7"869*+"#$"8#:*(";::*:")*+*,,-,"-./*01230" 4-+2*,"-./*01230"5./-67-8"9"3:"-./*01230"Figure 6: Cluster Expansion Performance7.4 PerformanceIn this section, we present performance results with work-loads representing MailBoxDB use cases. All tests are run-ning against a single storage node.The ﬁrst workload we use is from the MailBoxDB use case.We ran tests with 2 data sets. The data sets diﬀers in thesize of the mailboxes. Set A is for small mailbox test, eachmailbox having 100 messages and a total of 400,000 mail-boxes. Set B is for large mailbox test, each mailbox having50K messages and a total of 800 mailboxes. The Messageschema has 39 ﬁelds, 4 of which are indexed. The workloadsimulates 1% users having concurrent sessions, each sessionlasting 10 requests. Users are selected uniformly randomly.In this set of tests, we compare two index implementations,namely Lucene and Preﬁx-index as described in Section 5.1.In each test, we varied the read write ratio, and collect la-tency stats. For small mailbox, we run 2000 requests persecond, and for large mailbox, we run 250 requests per sec-ond. The results are shown below in Figure 7(a) and 7(b).For small mailboxes, Preﬁx index outperforms Lucene by

10.
!"#!"$!"%!"&!"!!(!" )*(#*" *!(*!" #*()*" !(!!"!"#$%&()*+,,+-$&.(/012341(5"67(+,-."/012,.,3" +,-."/45,673"889"/012,.,3" 889"/45,673"(a) Small MailBoxes!"#!"$!"%!"&!"!!"#!"$!"%!"!!(!" )*(#*" *!(*!" #*()*" !(!!"!"#$%&()*+,,+-$&.(/012341(5"67(+,-."/012,.,3" +,-."/45,673"889"/012,.,3" 889"/45,673"(b) Large MailBoxesFigure 7: Index Performance Comparison2x. The reason is for each PUT, Lucene invokes muchmore I/O than Preﬁx index because the Lucene index isimmutable, so new index ﬁles are generated, while Preﬁxindex does in-place update. The Preﬁx index is more sta-ble than Lucene. Its mean and 99% latency stays constantwhen the workload mix changes, while Lucene performancedegrades with more writes. This is because for Preﬁx index,the amount of I/O is ﬁxed for PUT and GET regardless ofthe mix. For Lucene, more PUT causes index fragmenta-tion, and hence degrade GET performance. Lucene also hascompaction overhead for garbage collection, which makesthe 99% latency much higher.For large mailboxes, Preﬁx index outperforms Lucene by1.8 to 2x. Lucene mean latency is more stable when work-load mix changes with large mailboxes compared with smallmailboxes, because for large mailboxes, GET and PUT la-tency is quite similar. The reason is with large mailboxes,there is a big index segment per mailbox with a few smalldelta segments. So both GET and PUT latency is domi-nated by access to the big segment. The Preﬁx index meanlatency is better when there is more PUTs, because GETsare more expansive than PUTs, as they needs to read in allinverted list rows. However, Lucene has better 99% latencythan Preﬁx index. This is because Preﬁx index starts atransaction, fetches the inverted index rows, modiﬁes them,then write them back. Because the mailboxes are big, sothe PUT cost is higher, and hence the transaction spanslonger period. With concurrent users, transactions can col-lide on locking the same rows. In real COMM application,the chance that the same user update his/her own mailboxis very rare. So in practice, this problem is not very cru-cial. However, we still plan to mitigate this problem by thefollowing improvement: we push down the inverted indexmutation to MySQL, to avoid the overhead of parsing andclient/server I/O, so the transaction time is much shorter,and hence the possibility of collision is much smaller.8. LESSONS LEARNEDThe deployment and testing experience with Espresso hasprovided us several valuable lessons in the area of high-availability and performance tuning in various parts of thesystem stack.Availability: As we have described in this paper, Espressouses master-slave architecture where the master handles writes(and fresh reads). When there are node failures, the clustermanager transfers the mastership to surviving replicas aftera conﬁgured timeout. To avoid ﬂapping where mastership isrepeatedly transferred due to transient failures, this is set toa few seconds. During this period, certain partitions becomebrieﬂy unavailable. We have allowed slave reads to addressread unavailability but the partitions remain unavailable forwrites till a new master is chosen. This is signiﬁcant outagefor some applications and we are working on reducing thisdowntime.Storage Devices: One of the big challenge of buildinga data platform at Linkedin is the large variety of use-casesand their access patterns. To enable multi-tenancy whileguaranteeing application SLAs required a lot of system tun-ing. The use of SSDs and their massive IOPs capacity whichreduce the dependency on buﬀer caches and the locality ofaccess, has been very eﬀective. In fact, we found that SSDschallenged a lot of the conventional wisdom including cost-eﬀectiveness. For example, the mailbox usecase has a largedata footprint and is storage bound. But to satisfy the re-quest pattern of this usecase, especially for secondary in-dexes, we found that it was in fact cheaper to use SSDs asSAS disks would have cost much more for the same IOPs.Java Tuning: Espresso has been developed in Java andwhile this has allowed for accelerated development, JVMtuning has been a challenge especially with mixed work-loads. Use of SSDs in fact exacerbates this problem as SSDsallow the data to be read at a very high rate, draining freememory and causing Linux to swap out memory pages ofthe JVM heap. We had to invest signiﬁcant time in JVMtuning to address this issue. Using InnoDB as the storageengine has also helped since the bulk of the data is held inInnoDB buﬀer pool which is outside the JVM heap. Duringperformance tuning, we also found that the complex inter-action between diﬀerent components make it rather diﬃcultto analyze performance bottlenecks using oﬀ the shelf tools.To address this, we ended up building a performance tool-ing framework that allows developers to automate runs withtheir code and visualize the impact of their changes on per-formance.

11.
Cluster Management: Apache Helix (the generic cluster-manager used by Espresso), uses Zookeeper as the coordina-tion service to store cluster metadata. Although Zookeepersupports a high throughput using sequential log writes, wefound the latencies to be high even with dedicated diskdrives. This is particularly signiﬁcant during fail-over, espe-cially when the number of aﬀected partitions is very high.We have made a number of improvements in Helix like groupcommit that improve the latency of zookeeper operations,which has allowed us to scale to tens of thousands of parti-tions in a single cluster. We plan further improvements inthis area that will allow us to scale even more.9. FUTURE WORKCross-entity transactions. Espresso’s hierarchical datamodel allows related entities in a collection to be updatedconsistently. This satisﬁed the requirements of a large num-ber of usecases, which model entity-child relationships. Inthe few cases that model entity-entity edge relationship andrequire both the edges to be modiﬁed atomically, we cur-rently do not provide this support and they risk seeing in-consistent data at times. We plan to add cross entity trans-action support in the future to address this requirement.Platform extensibility. Espresso was designed to be ageneric platform for building distributed data systems. Sofar, we have focused on building a OLTP system as describedin the paper. MySQL/InnoDB have proven to be very re-liable and reusing MySQLs capabilities of binary log andreplication have allowed for a very aggressive developmentschedule. There are many other usecases we plan to ad-dress with the platform. We have some ongoing work to useEspresso to build an OLAP system using a columnar stor-age engine. We also plan to use a log-structured engine toaddress usecases that have very high write rates.Multi-master deployment. We are currently workingon extending Espresso to be active in multiple data centersso that data is both readable and writable in multiple datacenters. The latency between LinkedIn data centers is typi-cally signiﬁcantly greater than the latency for a local write.Thus coordination across data centers for reads or writes itnot acceptable. In the multi-DC deployment, we plan torelax cross-colo consistency and service reads and writes lo-cally in the requesting data center to avoid degrading theuser experience.Most individual user-generated data at LinkedIn is onlywritable by a single, logged in, member. To provide read-your-writes consistency, we need to avoid accepting a writefrom a member in one data center then servicing a subse-quent read from the member from a diﬀerent data centerbefore the write has propagated. In the event the same rowwas updated in two data centers within the propagation la-tency, the applier of remotely originated writes applies aLast Writer Wins conﬂict resolution strategy to guaranteethat the row resolves to the same value in all regions.To minimize the likelihood of write conﬂicts we are devel-oping inter-data center sticky routing by member id. Whena member ﬁrst connects to LinkedIn, a DNS server will selecta data center based on geoproximity and current networkconditions. Upon login, the member will be issued a cookieindicating the data center that accepted the login. All sub-sequent access by the same member, within a TTL, will beserviced from the same region, redirecting if necessary.10. RELATED WORKEspresso provides a document oriented hierarchical datamodel that’s similar to that provided by Megastore [9] andSpanner [13]. Unlike MegaStore and Spanner, it does notprovide support for global distributed transactions but thetransaction support within an Entity group oﬀered by Espressois richer than most other distributed data systems such asMongoDB [5], HBase [1] and PNUTS [12]. Among the well-known NoSQL systems, MongoDB is the only one that oﬀersrich secondary indexing capability at par with Espresso, al-though it lags Espresso in terms of RAM:disk utilization.With the exception of MongoDB, other NoSQL systems donot oﬀer rich secondary indexing capability that Espressooﬀers.Like BigTable [11] and HBase [1], Espresso chooses CAover AP in contrast to most Dynamo style systems such asRiak and Voldemort. However, HBase and BigTable fol-low a shared-storage paradigm by using a distributed repli-cated ﬁle system for storing data blocks. Espresso uses localshared nothing storage and log shipping between mastersand slaves with automatic failover, similar to MongoDB.This guarantees that queries are always served out of localstorage and delivers better latency on write operations.The multi-DC operation of Espresso diﬀers signiﬁcantlyfrom other systems. Eventually consistent systems like Volde-mort [6] and Cassandra [17] implement quorums that spangeographic regions. MegaStore and Spanner implement syn-chronous replica maintenance across data centers using Paxos.PNUTS implement record level mastership and allows writesonly on the geographic master. Espresso relaxes consistencyacross data centers and allows concurrent writes to the samedata in multiple data centers relying on the application lay-ers to minimize write conﬂicts. It then employs conﬂict de-tection and resolution schemes within the system to ensurethat data in diﬀerent data centers eventually converges.11. CONCLUSIONWe have described Espresso, a distributed document-orienteddatabase that emerged out of our need to build a primarysource-of-truth data store for LinkedIn. Espresso is timelineconsistent, provides rich operations on documents includ-ing transactions over related documents, secondary indexedaccess and supports seamless integration with nearline andoﬄine environments. Espresso has been in production sinceJune 2012 serving several key use-cases.We are in the process of implementing several additionalEspresso features such as support for global secondary in-dexes, accepting writes in multiple data centers with multi-ple master replicas and supporting more complex data struc-tures such as lists and sets natively. As we scale our applica-tion footprint, we’re also starting to work on multi-tenancychallenges to simplify our capacity planning and hardwarefootprint story.AcknowledgementMany other members of the Linkedin Data Infrastructureteam helped signiﬁcantly in the development and deploy-ment of Espresso. We would like to thank the followingpeople for their invaluable contributions: Chavdar Botev,Phanindra Ganti and Boris Shkolnik from the Databus teamworked very closely with us. Eun-Gyu Kim and KrishnaPuttaswamy Naga have joined the Espresso team recently.