blob: 17766b598b4ddde051805593ac2b29597e149a04 [file] [log] [blame]
Mark Slee24b49d32007-03-21 01:24:00 +00001%-----------------------------------------------------------------------------
2%
3% Thrift whitepaper
4%
5% Name: thrift.tex
6%
7% Authors: Mark Slee (mcslee@facebook.com)
8%
9% Created: 05 March 2007
10%
11%-----------------------------------------------------------------------------
12
13
14\documentclass[nocopyrightspace,blockstyle]{sigplanconf}
15
16\usepackage{amssymb}
17\usepackage{amsfonts}
18\usepackage{amsmath}
Marc Slemko10b3bdb2007-04-01 09:14:05 +000019\usepackage{url}
Mark Slee24b49d32007-03-21 01:24:00 +000020
21\begin{document}
22
David Reiss0c90f6f2008-02-06 22:18:40 +000023% \conferenceinfo{WXYZ '05}{date, City.}
Mark Slee24b49d32007-03-21 01:24:00 +000024% \copyrightyear{2007}
David Reiss0c90f6f2008-02-06 22:18:40 +000025% \copyrightdata{[to be supplied]}
Mark Slee24b49d32007-03-21 01:24:00 +000026
27% \titlebanner{banner above paper title} % These are ignored unless
28% \preprintfooter{short description of paper} % 'preprint' option specified.
29
30\title{Thrift: Scalable Cross-Language Services Implementation}
31\subtitle{}
32
33\authorinfo{Mark Slee, Aditya Agarwal and Marc Kwiatkowski}
34 {Facebook, 156 University Ave, Palo Alto, CA}
35 {\{mcslee,aditya,marc\}@facebook.com}
36
37\maketitle
38
39\begin{abstract}
40Thrift is a software library and set of code-generation tools developed at
41Facebook to expedite development and implementation of efficient and scalable
42backend services. Its primary goal is to enable efficient and reliable
43communication across programming languages by abstracting the portions of each
44language that tend to require the most customization into a common library
45that is implemented in each language. Specifically, Thrift allows developers to
Mark Sleeafaf2762007-04-03 19:47:04 +000046define datatypes and service interfaces in a single language-neutral file
Mark Slee24b49d32007-03-21 01:24:00 +000047and generate all the necessary code to build RPC clients and servers.
48
49This paper details the motivations and design choices we made in Thrift, as
50well as some of the more interesting implementation details. It is not
51intended to be taken as research, but rather it is an exposition on what we did
52and why.
53\end{abstract}
54
55% \category{D.3.3}{Programming Languages}{Language constructs and features}
56
57%\terms
58%Languages, serialization, remote procedure call
59
60%\keywords
61%Data description language, interface definition language, remote procedure call
62
63\section{Introduction}
64As Facebook's traffic and network structure have scaled, the resource
David Reiss0c90f6f2008-02-06 22:18:40 +000065demands of many operations on the site (i.e. search,
Mark Slee24b49d32007-03-21 01:24:00 +000066ad selection and delivery, event logging) have presented technical requirements
67drastically outside the scope of the LAMP framework. In our implementation of
68these services, various programming languages have been selected to
69optimize for the right combination of performance, ease and speed of
70development, availability of existing libraries, etc. By and large,
71Facebook's engineering culture has tended towards choosing the best
Mark Sleeafaf2762007-04-03 19:47:04 +000072tools and implementations available over standardizing on any one
Mark Slee24b49d32007-03-21 01:24:00 +000073programming language and begrudgingly accepting its inherent limitations.
74
75Given this design choice, we were presented with the challenge of building
76a transparent, high-performance bridge across many programming languages.
77We found that most available solutions were either too limited, did not offer
Mark Sleeafaf2762007-04-03 19:47:04 +000078sufficient datatype freedom, or suffered from subpar performance.
Mark Slee24b49d32007-03-21 01:24:00 +000079\footnote{See Appendix A for a discussion of alternative systems.}
80
81The solution that we have implemented combines a language-neutral software
82stack implemented across numerous programming languages and an associated code
83generation engine that transforms a simple interface and data definition
84language into client and server remote procedure call libraries.
85Choosing static code generation over a dynamic system allows us to create
Mark Sleeafaf2762007-04-03 19:47:04 +000086validated code that can be run without the need for
87any advanced introspective run-time type checking. It is also designed to
Mark Slee24b49d32007-03-21 01:24:00 +000088be as simple as possible for the developer, who can typically define all
89the necessary data structures and interfaces for a complex service in a single
90short file.
91
92Surprised that a robust open solution to these relatively common problems
93did not yet exist, we committed early on to making the Thrift implementation
94open source.
95
96In evaluating the challenges of cross-language interaction in a networked
97environment, some key components were identified:
98
99\textit{Types.} A common type system must exist across programming languages
Mark Sleeafaf2762007-04-03 19:47:04 +0000100without requiring that the application developer use custom Thrift datatypes
Mark Slee24b49d32007-03-21 01:24:00 +0000101or write their own serialization code. That is,
102a C++ programmer should be able to transparently exchange a strongly typed
103STL map for a dynamic Python dictionary. Neither
104programmer should be forced to write any code below the application layer
David Reiss0c90f6f2008-02-06 22:18:40 +0000105to achieve this. Section 2 details the Thrift type system.
Mark Slee24b49d32007-03-21 01:24:00 +0000106
107\textit{Transport.} Each language must have a common interface to
108bidirectional raw data transport. The specifics of how a given
109transport is implemented should not matter to the service developer.
110The same application code should be able to run against TCP stream sockets,
111raw data in memory, or files on disk. Section 3 details the Thrift Transport
112layer.
113
Mark Sleeafaf2762007-04-03 19:47:04 +0000114\textit{Protocol.} Datatypes must have some way of using the Transport
Mark Slee24b49d32007-03-21 01:24:00 +0000115layer to encode and decode themselves. Again, the application
116developer need not be concerned by this layer. Whether the service uses
117an XML or binary protocol is immaterial to the application code.
118All that matters is that the data can be read and written in a consistent,
119deterministic matter. Section 4 details the Thrift Protocol layer.
120
Mark Sleeafaf2762007-04-03 19:47:04 +0000121\textit{Versioning.} For robust services, the involved datatypes must
Mark Slee24b49d32007-03-21 01:24:00 +0000122provide a mechanism for versioning themselves. Specifically,
123it should be possible to add or remove fields in an object or alter the
124argument list of a function without any interruption in service (or,
125worse yet, nasty segmentation faults). Section 5 details Thrift's versioning
126system.
127
128\textit{Processors.} Finally, we generate code capable of processing data
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000129streams to accomplish remote procedure calls. Section 6 details the generated
Mark Slee24b49d32007-03-21 01:24:00 +0000130code and TProcessor paradigm.
131
132Section 7 discusses implementation details, and Section 8 describes
133our conclusions.
134
135\section{Types}
136
137The goal of the Thrift type system is to enable programmers to develop using
138completely natively defined types, no matter what programming language they
139use. By design, the Thrift type system does not introduce any special dynamic
140types or wrapper objects. It also does not require that the developer write
Mark Sleeafaf2762007-04-03 19:47:04 +0000141any code for object serialization or transport. The Thrift IDL (Interface
142Definition Language) file is
Mark Slee24b49d32007-03-21 01:24:00 +0000143logically a way for developers to annotate their data structures with the
144minimal amount of extra information necessary to tell a code generator
145how to safely transport the objects across languages.
146
147\subsection{Base Types}
148
149The type system rests upon a few base types. In considering which types to
150support, we aimed for clarity and simplicity over abundance, focusing
151on the key types available in all programming languages, ommitting any
152niche types available only in specific languages.
153
154The base types supported by Thrift are:
155\begin{itemize}
156\item \texttt{bool} A boolean value, true or false
157\item \texttt{byte} A signed byte
158\item \texttt{i16} A 16-bit signed integer
159\item \texttt{i32} A 32-bit signed integer
160\item \texttt{i64} A 64-bit signed integer
161\item \texttt{double} A 64-bit floating point number
162\item \texttt{string} An encoding-agnostic text or binary string
163\end{itemize}
164
165Of particular note is the absence of unsigned integer types. Because these
166types have no direct translation to native primitive types in many languages,
167the advantages they afford are lost. Further, there is no way to prevent the
168application developer in a language like Python from assigning a negative value
169to an integer variable, leading to unpredictable behavior. From a design
170standpoint, we observed that unsigned integers were very rarely, if ever, used
171for arithmetic purposes, but in practice were much more often used as keys or
172identifiers. In this case, the sign is irrelevant. Signed integers serve this
173same purpose and can be safely cast to their unsigned counterparts (most
174commonly in C++) when absolutely necessary.
175
Mark Slee24b49d32007-03-21 01:24:00 +0000176\subsection{Structs}
177
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000178A Thrift struct defines a common object to be used across languages. A struct
Mark Slee24b49d32007-03-21 01:24:00 +0000179is essentially equivalent to a class in object oriented programming
180languages. A struct has a set of strongly typed fields, each with a unique
181name identifier. The basic syntax for defining a Thrift struct looks very
182similar to a C struct definition. Fields may be annotated with an integer field
183identifier (unique to the scope of that struct) and optional default values.
184Field identifiers will be automatically assigned if omitted, though they are
185strongly encouraged for versioning reasons discussed later.
186
Mark Sleeafaf2762007-04-03 19:47:04 +0000187\subsection{Containers}
188
189Thrift containers are strongly typed containers that map to the most commonly
190used containers in common programming languages. They are annotated using
191the C++ template (or Java Generics) style. There are three types available:
192\begin{itemize}
193\item \texttt{list<type>} An ordered list of elements. Translates directly into
194an STL \texttt{vector}, Java \texttt{ArrayList}, or native array in scripting languages. May
195contain duplicates.
196\item \texttt{set<type>} An unordered set of unique elements. Translates into
197an STL \texttt{set}, Java \texttt{HashSet}, \texttt{set} in Python, or native
David Reiss0c90f6f2008-02-06 22:18:40 +0000198dictionary in PHP/Ruby.
Mark Sleeafaf2762007-04-03 19:47:04 +0000199\item \texttt{map<type1,type2>} A map of strictly unique keys to values
Mark Slee09bfd612007-04-04 19:56:41 +0000200Translates into an STL \texttt{map}, Java \texttt{HashMap}, PHP associative
Mark Sleeafaf2762007-04-03 19:47:04 +0000201array, or Python/Ruby dictionary.
202\end{itemize}
203
204While defaults are provided, the type mappings are not explicitly fixed. Custom
205code generator directives have been added to substitute custom types in
206destination languages (i.e.
207\texttt{hash\_map} or Google's sparse hash map can be used in C++). The
208only requirement is that the custom types support all the necessary iteration
209primitives. Container elements may be of any valid Thrift type, including other
210containers or structs.
211
Mark Slee24b49d32007-03-21 01:24:00 +0000212\begin{verbatim}
213struct Example {
214 1:i32 number=10,
215 2:i64 bigNumber,
216 3:double decimals,
217 4:string name="thrifty"
218}\end{verbatim}
219
220In the target language, each definition generates a type with two methods,
221\texttt{read} and \texttt{write}, which perform serialization and transport
222of the objects using a Thrift TProtocol object.
223
224\subsection{Exceptions}
225
226Exceptions are syntactically and functionally equivalent to structs except
227that they are declared using the \texttt{exception} keyword instead of the
228\texttt{struct} keyword.
229
230The generated objects inherit from an exception base class as appropriate
Mark Sleeafaf2762007-04-03 19:47:04 +0000231in each target programming language, in order to seamlessly
232integrate with native exception handling in any given
Mark Slee24b49d32007-03-21 01:24:00 +0000233language. Again, the design emphasis is on making the code familiar to the
234application developer.
235
236\subsection{Services}
237
238Services are defined using Thrift types. Definition of a service is
Mark Sleeafaf2762007-04-03 19:47:04 +0000239semantically equivalent to defining an interface (or a pure virtual abstract
240class) in object oriented
Mark Slee24b49d32007-03-21 01:24:00 +0000241programming. The Thrift compiler generates fully functional client and
242server stubs that implement the interface. Services are defined as follows:
243
244\begin{verbatim}
245service <name> {
246 <returntype> <name>(<arguments>)
247 [throws (<exceptions>)]
248 ...
249}\end{verbatim}
250
251An example:
252
253\begin{verbatim}
254service StringCache {
255 void set(1:i32 key, 2:string value),
256 string get(1:i32 key) throws (1:KeyNotFound knf),
David Reiss0c90f6f2008-02-06 22:18:40 +0000257 void delete(1:i32 key)
Mark Slee24b49d32007-03-21 01:24:00 +0000258}
259\end{verbatim}
260
261Note that \texttt{void} is a valid type for a function return, in addition to
262all other defined Thrift types. Additionally, an \texttt{async} modifier
Mark Sleeafaf2762007-04-03 19:47:04 +0000263keyword may be added to a \texttt{void} function, which will generate code that does
Mark Slee24b49d32007-03-21 01:24:00 +0000264not wait for a response from the server. Note that a pure \texttt{void}
265function will return a response to the client which guarantees that the
266operation has completed on the server side. With \texttt{async} method calls
Mark Sleeafaf2762007-04-03 19:47:04 +0000267the client will only be guaranteed that the request succeeded at the
Mark Slee24b49d32007-03-21 01:24:00 +0000268transport layer. (In many transport scenarios this is inherently unreliable
269due to the Byzantine Generals' Problem. Therefore, application developers
Mark Sleeafaf2762007-04-03 19:47:04 +0000270should take care only to use the async optimization in cases where dropped
Mark Slee24b49d32007-03-21 01:24:00 +0000271method calls are acceptable or the transport is known to be reliable.)
272
Mark Sleeafaf2762007-04-03 19:47:04 +0000273Also of note is the fact that argument lists and exception lists for functions
274are implemented as Thrift structs. All three constructs are identical in both
275notation and behavior.
Mark Slee24b49d32007-03-21 01:24:00 +0000276
277\section{Transport}
278
279The transport layer is used by the generated code to facilitate data transfer.
280
281\subsection{Interface}
282
Mark Sleeafaf2762007-04-03 19:47:04 +0000283A key design choice in the implementation of Thrift was to decouple the
Mark Slee24b49d32007-03-21 01:24:00 +0000284transport layer from the code generation layer. Though Thrift is typically
285used on top of the TCP/IP stack with streaming sockets as the base layer of
David Reiss0c90f6f2008-02-06 22:18:40 +0000286communication, there was no compelling reason to build that constraint into
Mark Slee24b49d32007-03-21 01:24:00 +0000287the system. The performance tradeoff incurred by an abstracted I/O layer
288(roughly one virtual method lookup / function call per operation) was
289immaterial compared to the cost of actual I/O operations (typically invoking
290system calls).
291
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000292Fundamentally, generated Thrift code only needs to know how to read and
Mark Sleeafaf2762007-04-03 19:47:04 +0000293write data. The origin and destination of the data are irrelevant; it may be a
294socket, a segment of shared memory, or a file on the local disk. The Thrift
295transport interface supports the following methods:
Mark Slee24b49d32007-03-21 01:24:00 +0000296
297\begin{itemize}
Mark Sleeafaf2762007-04-03 19:47:04 +0000298\item \texttt{open} Opens the tranpsort
299\item \texttt{close} Closes the tranport
300\item \texttt{isOpen} Indicates whether the transport is open
301\item \texttt{read} Reads from the transport
302\item \texttt{write} Writes to the transport
303\item \texttt{flush} Forces any pending writes
Mark Slee24b49d32007-03-21 01:24:00 +0000304\end{itemize}
305
306There are a few additional methods not documented here which are used to aid
Mark Sleeafaf2762007-04-03 19:47:04 +0000307in batching reads and optionally signaling the completion of a read or
308write operation from the generated code.
Mark Slee24b49d32007-03-21 01:24:00 +0000309
310In addition to the above
Mark Sleec11045a2007-04-01 23:14:38 +0000311\texttt{TTransport} interface, there is a\\
312\texttt{TServerTransport} interface
Mark Slee24b49d32007-03-21 01:24:00 +0000313used to accept or create primitive transport objects. Its interface is as
314follows:
315
316\begin{itemize}
Mark Sleeafaf2762007-04-03 19:47:04 +0000317\item \texttt{open} Opens the transport
318\item \texttt{listen} Begins listening for connections
319\item \texttt{accept} Returns a new client transport
320\item \texttt{close} Closes the transport
Mark Slee24b49d32007-03-21 01:24:00 +0000321\end{itemize}
322
323\subsection{Implementation}
324
325The transport interface is designed for simple implementation in any
326programming language. New transport mechanisms can be easily defined as needed
327by application developers.
328
329\subsubsection{TSocket}
330
331The \texttt{TSocket} class is implemented across all target languages. It
332provides a common, simple interface to a TCP/IP stream socket.
333
334\subsubsection{TFileTransport}
335
336The \texttt{TFileTransport} is an abstraction of an on-disk file to a data
Mark Sleeafaf2762007-04-03 19:47:04 +0000337stream. It can be used to write out a set of incoming Thrift requests to a file
338on disk. The on-disk data can then be replayed from the log, either for
339post-processing or for reproduction and/or simulation of past events.
Mark Slee24b49d32007-03-21 01:24:00 +0000340
341\subsubsection{Utilities}
342
343The Transport interface is designed to support easy extension using common
Mark Sleeafaf2762007-04-03 19:47:04 +0000344OOP techniques, such as composition. Some simple utilites include the
345\texttt{TBufferedTransport}, which buffers the writes and reads on an
346underlying transport, the \texttt{TFramedTransport}, which transmits data with frame
347size headers for chunking optimization or nonblocking operation, and the
348\texttt{TMemoryBuffer}, which allows reading and writing directly from the heap
349or stack memory owned by the process.
Mark Slee24b49d32007-03-21 01:24:00 +0000350
351\section{Protocol}
352
353A second major abstraction in Thrift is the separation of data structure from
354transport representation. Thrift enforces a certain messaging structure when
355transporting data, but it is agnostic to the protocol encoding in use. That is,
Mark Sleeafaf2762007-04-03 19:47:04 +0000356it does not matter whether data is encoded as XML, human-readable ASCII, or a
357dense binary format as long as the data supports a fixed set of operations
358that allow it to be deterministically read and written by generated code.
Mark Slee24b49d32007-03-21 01:24:00 +0000359
360\subsection{Interface}
361
362The Thrift Protocol interface is very straightforward. It fundamentally
363supports two things: 1) bidirectional sequenced messaging, and
3642) encoding of base types, containers, and structs.
365
366\begin{verbatim}
367writeMessageBegin(name, type, seq)
368writeMessageEnd()
369writeStructBegin(name)
370writeStructEnd()
371writeFieldBegin(name, type, id)
372writeFieldEnd()
373writeFieldStop()
374writeMapBegin(ktype, vtype, size)
375writeMapEnd()
376writeListBegin(etype, size)
377writeListEnd()
378writeSetBegin(etype, size)
379writeSetEnd()
380writeBool(bool)
381writeByte(byte)
382writeI16(i16)
383writeI32(i32)
384writeI64(i64)
385writeDouble(double)
386writeString(string)
387
388name, type, seq = readMessageBegin()
389 readMessageEnd()
390name = readStructBegin()
391 readStructEnd()
392name, type, id = readFieldBegin()
393 readFieldEnd()
394k, v, size = readMapBegin()
395 readMapEnd()
396etype, size = readListBegin()
397 readListEnd()
398etype, size = readSetBegin()
399 readSetEnd()
400bool = readBool()
401byte = readByte()
402i16 = readI16()
403i32 = readI32()
404i64 = readI64()
405double = readDouble()
406string = readString()
407\end{verbatim}
408
Mark Sleeafaf2762007-04-03 19:47:04 +0000409Note that every \texttt{write} function has exactly one \texttt{read} counterpart, with
410the exception of \texttt{writeFieldStop()}. This is a special method
Mark Slee24b49d32007-03-21 01:24:00 +0000411that signals the end of a struct. The procedure for reading a struct is to
Mark Sleeafaf2762007-04-03 19:47:04 +0000412\texttt{readFieldBegin()} until the stop field is encountered, and then to
Mark Slee24b49d32007-03-21 01:24:00 +0000413\texttt{readStructEnd()}. The
Mark Sleeafaf2762007-04-03 19:47:04 +0000414generated code relies upon this call sequence to ensure that everything written by
Mark Slee24b49d32007-03-21 01:24:00 +0000415a protocol encoder can be read by a matching protocol decoder. Further note
416that this set of functions is by design more robust than necessary.
417For example, \texttt{writeStructEnd()} is not strictly necessary, as the end of
418a struct may be implied by the stop field. This method is a convenience for
Mark Sleeafaf2762007-04-03 19:47:04 +0000419verbose protocols in which it is cleaner to separate these calls (e.g. a closing
Mark Slee24b49d32007-03-21 01:24:00 +0000420\texttt{</struct>} tag in XML).
421
422\subsection{Structure}
423
424Thrift structures are designed to support encoding into a streaming
Mark Sleeafaf2762007-04-03 19:47:04 +0000425protocol. The implementation should never need to frame or compute the
Mark Slee24b49d32007-03-21 01:24:00 +0000426entire data length of a structure prior to encoding it. This is critical to
427performance in many scenarios. Consider a long list of relatively large
Mark Sleeafaf2762007-04-03 19:47:04 +0000428strings. If the protocol interface required reading or writing a list to be an
429atomic operation, then the implementation would need to perform a linear pass over the
Mark Slee24b49d32007-03-21 01:24:00 +0000430entire list before encoding any data. However, if the list can be written
431as iteration is performed, the corresponding read may begin in parallel,
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000432theoretically offering an end-to-end speedup of $(kN - C)$, where $N$ is the size
Mark Slee24b49d32007-03-21 01:24:00 +0000433of the list, $k$ the cost factor associated with serializing a single
434element, and $C$ is fixed offset for the delay between data being written
435and becoming available to read.
436
437Similarly, structs do not encode their data lengths a priori. Instead, they are
438encoded as a sequence of fields, with each field having a type specifier and a
Mark Sleeafaf2762007-04-03 19:47:04 +0000439unique field identifier. Note that the inclusion of type specifiers allows
Mark Slee24b49d32007-03-21 01:24:00 +0000440the protocol to be safely parsed and decoded without any generated code
441or access to the original IDL file. Structs are terminated by a field header
442with a special \texttt{STOP} type. Because all the basic types can be read
Mark Sleeafaf2762007-04-03 19:47:04 +0000443deterministically, all structs (even those containing other structs) can be
Mark Slee24b49d32007-03-21 01:24:00 +0000444read deterministically. The Thrift protocol is self-delimiting without any
445framing and regardless of the encoding format.
446
447In situations where streaming is unnecessary or framing is advantageous, it
448can be very simply added into the transport layer, using the
449\texttt{TFramedTransport} abstraction.
450
451\subsection{Implementation}
452
453Facebook has implemented and deployed a space-efficient binary protocol which
454is used by most backend services. Essentially, it writes all data
455in a flat binary format. Integer types are converted to network byte order,
456strings are prepended with their byte length, and all message and field headers
457are written using the primitive integer serialization constructs. String names
458for fields are omitted - when using generated code, field identifiers are
459sufficient.
460
461We decided against some extreme storage optimizations (i.e. packing
462small integers into ASCII or using a 7-bit continuation format) for the sake
463of simplicity and clarity in the code. These alterations can easily be made
Mark Sleeafaf2762007-04-03 19:47:04 +0000464if and when we encounter a performance-critical use case that demands them.
Mark Slee24b49d32007-03-21 01:24:00 +0000465
466\section{Versioning}
467
468Thrift is robust in the face of versioning and data definition changes. This
Mark Sleeafaf2762007-04-03 19:47:04 +0000469is critical to enable staged rollouts of changes to deployed services. The
470system must be able to support reading of old data from log files, as well as
471requests from out-of-date clients to new servers, and vice versa.
Mark Slee24b49d32007-03-21 01:24:00 +0000472
473\subsection{Field Identifiers}
474
475Versioning in Thrift is implemented via field identifiers. The field header
476for every member of a struct in Thrift is encoded with a unique field
477identifier. The combination of this field identifier and its type specifier
478is used to uniquely identify the field. The Thrift definition language
479supports automatic assignment of field identifiers, but it is good
480programming practice to always explicitly specify field identifiers.
481Identifiers are specified as follows:
482
483\begin{verbatim}
484struct Example {
485 1:i32 number=10,
486 2:i64 bigNumber,
487 3:double decimals,
488 4:string name="thrifty"
489}\end{verbatim}
490
Mark Sleeafaf2762007-04-03 19:47:04 +0000491To avoid conflicts between manually and automatically assigned identifiers,
492fields with identifiers omitted are assigned identifiers
Mark Slee24b49d32007-03-21 01:24:00 +0000493decrementing from -1, and the language only supports the manual assignment of
494positive identifiers.
495
496When data is being deserialized, the generated code can use these identifiers
497to properly identify the field and determine whether it aligns with a field in
498its definition file. If a field identifier is not recognized, the generated
499code can use the type specifier to skip the unknown field without any error.
Mark Sleeafaf2762007-04-03 19:47:04 +0000500Again, this is possible due to the fact that all datatypes are self
Mark Slee24b49d32007-03-21 01:24:00 +0000501delimiting.
502
503Field identifiers can (and should) also be specified in function argument
504lists. In fact, argument lists are not only represented as structs on the
505backend, but actually share the same code in the compiler frontend. This
506allows for version-safe modification of method parameters
507
508\begin{verbatim}
509service StringCache {
510 void set(1:i32 key, 2:string value),
511 string get(1:i32 key) throws (1:KeyNotFound knf),
David Reiss0c90f6f2008-02-06 22:18:40 +0000512 void delete(1:i32 key)
Mark Slee24b49d32007-03-21 01:24:00 +0000513}
514\end{verbatim}
515
516The syntax for specifying field identifiers was chosen to echo their structure.
517Structs can be thought of as a dictionary where the identifiers are keys, and
Mark Sleeafaf2762007-04-03 19:47:04 +0000518the values are strongly-typed named fields.
Mark Slee24b49d32007-03-21 01:24:00 +0000519
520Field identifiers internally use the \texttt{i16} Thrift type. Note, however,
521that the \texttt{TProtocol} abstraction may encode identifiers in any format.
522
523\subsection{Isset}
524
525When an unexpected field is encountered, it can be safely ignored and
526discarded. When an expected field is not found, there must be some way to
527signal to the developer that it was not present. This is implemented via an
Mark Sleeafaf2762007-04-03 19:47:04 +0000528inner \texttt{isset} structure inside the defined objects. (Isset functionality
529is implicit with a \texttt{null} value in PHP, \texttt{None} in Python
Mark Slee24b49d32007-03-21 01:24:00 +0000530and \texttt{nil} in Ruby.) Essentially,
531the inner \texttt{isset} object of each Thrift struct contains a boolean value
532for each field which denotes whether or not that field is present in the
533struct. When a reader receives a struct, it should check for a field being set
534before operating directly on it.
535
536\begin{verbatim}
537class Example {
538 public:
539 Example() :
540 number(10),
541 bigNumber(0),
542 decimals(0),
David Reiss0c90f6f2008-02-06 22:18:40 +0000543 name("thrifty") {}
Mark Slee24b49d32007-03-21 01:24:00 +0000544
545 int32_t number;
546 int64_t bigNumber;
547 double decimals;
548 std::string name;
549
550 struct __isset {
551 __isset() :
552 number(false),
553 bigNumber(false),
554 decimals(false),
555 name(false) {}
556 bool number;
557 bool bigNumber;
558 bool decimals;
559 bool name;
560 } __isset;
561...
562}
David Reiss0c90f6f2008-02-06 22:18:40 +0000563\end{verbatim}
Mark Slee24b49d32007-03-21 01:24:00 +0000564
565\subsection{Case Analysis}
566
567There are four cases in which version mismatches may occur.
568
569\begin{enumerate}
570\item \textit{Added field, old client, new server.} In this case, the old
571client does not send the new field. The new server recognizes that the field
Mark Sleeafaf2762007-04-03 19:47:04 +0000572is not set, and implements default behavior for out-of-date requests.
Mark Slee24b49d32007-03-21 01:24:00 +0000573\item \textit{Removed field, old client, new server.} In this case, the old
574client sends the removed field. The new server simply ignores it.
575\item \textit{Added field, new client, old server.} The new client sends a
576field that the old server does not recognize. The old server simply ignores
577it and processes as normal.
578\item \textit{Removed field, new client, old server.} This is the most
579dangerous case, as the old server is unlikely to have suitable default
580behavior implemented for the missing field. It is recommended that in this
581situation the new server be rolled out prior to the new clients.
582\end{enumerate}
583
584\subsection{Protocol/Transport Versioning}
585The \texttt{TProtocol} abstractions are also designed to give protocol
586implementations the freedom to version themselves in whatever manner they
587see fit. Specifically, any protocol implementation is free to send whatever
588it likes in the \texttt{writeMessageBegin()} call. It is entirely up to the
589implementor how to handle versioning at the protocol level. The key point is
590that protocol encoding changes are safely isolated from interface definition
591version changes.
592
593Note that the exact same is true of the \texttt{TTransport} interface. For
594example, if we wished to add some new checksumming or error detection to the
595\texttt{TFileTransport}, we could simply add a version header into the
596data it writes to the file in such a way that it would still accept old
Mark Sleeafaf2762007-04-03 19:47:04 +0000597log files without the given header.
Mark Slee24b49d32007-03-21 01:24:00 +0000598
599\section{RPC Implementation}
600
601\subsection{TProcessor}
602
603The last core interface in the Thrift design is the \texttt{TProcessor},
604perhaps the most simple of the constructs. The interface is as follows:
605
606\begin{verbatim}
607interface TProcessor {
608 bool process(TProtocol in, TProtocol out)
609 throws TException
610}
611\end{verbatim}
612
613The key design idea here is that the complex systems we build can fundamentally
614be broken down into agents or services that operate on inputs and outputs. In
615most cases, there is actually just one input and output (an RPC client) that
616needs handling.
617
618\subsection{Generated Code}
619
620When a service is defined, we generate a
621\texttt{TProcessor} instance capable of handling RPC requests to that service,
622using a few helpers. The fundamental structure (illustrated in pseudo-C++) is
623as follows:
624
625\begin{verbatim}
626Service.thrift
627 => Service.cpp
628 interface ServiceIf
629 class ServiceClient : virtual ServiceIf
630 TProtocol in
631 TProtocol out
632 class ServiceProcessor : TProcessor
633 ServiceIf handler
634
635ServiceHandler.cpp
636 class ServiceHandler : virtual ServiceIf
637
638TServer.cpp
639 TServer(TProcessor processor,
640 TServerTransport transport,
641 TTransportFactory tfactory,
642 TProtocolFactory pfactory)
643 serve()
644\end{verbatim}
645
Mark Sleec11045a2007-04-01 23:14:38 +0000646From the Thrift definition file, we generate the virtual service interface.
Mark Slee24b49d32007-03-21 01:24:00 +0000647A client class is generated, which implements the interface and
648uses two \texttt{TProtocol} instances to perform the I/O operations. The
649generated processor implements the \texttt{TProcessor} interface. The generated
650code has all the logic to handle RPC invocations via the \texttt{process()}
Mark Sleeafaf2762007-04-03 19:47:04 +0000651call, and takes as a parameter an instance of the service interface, as
Mark Slee24b49d32007-03-21 01:24:00 +0000652implemented by the application developer.
653
Mark Sleeafaf2762007-04-03 19:47:04 +0000654The user provides an implementation of the application interface in separate,
655non-generated source code.
Mark Slee24b49d32007-03-21 01:24:00 +0000656
657\subsection{TServer}
658
659Finally, the Thrift core libraries provide a \texttt{TServer} abstraction.
660The \texttt{TServer} object generally works as follows.
661
662\begin{itemize}
663\item Use the \texttt{TServerTransport} to get a \texttt{TTransport}
664\item Use the \texttt{TTransportFactory} to optionally convert the primitive
665transport into a suitable application transport (typically the
666\texttt{TBufferedTransportFactory} is used here)
667\item Use the \texttt{TProtocolFactory} to create an input and output protocol
668for the \texttt{TTransport}
669\item Invoke the \texttt{process()} method of the \texttt{TProcessor} object
670\end{itemize}
671
672The layers are appropriately separated such that the server code needs to know
673nothing about any of the transports, encodings, or applications in play. The
674server encapsulates the logic around connection handling, threading, etc.
675while the processor deals with RPC. The only code written by the application
Mark Sleec11045a2007-04-01 23:14:38 +0000676developer lives in the definitional Thrift file and the interface
Mark Slee24b49d32007-03-21 01:24:00 +0000677implementation.
678
679Facebook has deployed multiple \texttt{TServer} implementations, including
680the single-threaded \texttt{TSimpleServer}, thread-per-connection
681\texttt{TThreadedServer}, and thread-pooling \texttt{TThreadPoolServer}.
682
683The \texttt{TProcessor} interface is very general by design. There is no
684requirement that a \texttt{TServer} take a generated \texttt{TProcessor}
685object. Thrift allows the application developer to easily write any type of
686server that operates on \texttt{TProtocol} objects (for instance, a server
687could simply stream a certain type of object without any actual RPC method
688invocation).
689
690\section{Implementation Details}
691\subsection{Target Languages}
692Thrift currently supports five target languages: C++, Java, Python, Ruby, and
693PHP. At Facebook, we have deployed servers predominantly in C++, Java, and
694Python. Thrift services implemented in PHP have also been embedded into the
695Apache web server, providing transparent backend access to many of our
696frontend constructs using a \texttt{THttpClient} implementation of the
697\texttt{TTransport} interface.
698
699Though Thrift was explicitly designed to be much more efficient and robust
700than typical web technologies, as we were designing our XML-based REST web
701services API we noticed that Thrift could be easily used to define our
702service interface. Though we do not currently employ SOAP envelopes (in the
Mark Sleeafaf2762007-04-03 19:47:04 +0000703authors' opinions there is already far too much repetitive enterprise Java
Mark Slee24b49d32007-03-21 01:24:00 +0000704software to do that sort of thing), we were able to quickly extend Thrift to
705generate XML Schema Definition files for our service, as well as a framework
706for versioning different implementations of our web service. Though public
707web services are admittedly tangential to Thrift's core use case and design,
708Thrift facilitated rapid iteration and affords us the ability to quickly
709migrate our entire XML-based web service onto a higher performance system
Mark Sleeafaf2762007-04-03 19:47:04 +0000710should the need arise.
Mark Slee24b49d32007-03-21 01:24:00 +0000711
712\subsection{Generated Structs}
713We made a conscious decision to make our generated structs as transparent as
714possible. All fields are publicly accessible; there are no \texttt{set()} and
715\texttt{get()} methods. Similarly, use of the \texttt{isset} object is not
716enforced. We do not include any \texttt{FieldNotSetException} construct.
717Developers have the option to use these fields to write more robust code, but
718the system is robust to the developer ignoring the \texttt{isset} construct
719entirely and will provide suitable default behavior in all cases.
720
Mark Sleeafaf2762007-04-03 19:47:04 +0000721This choice was motivated by the desire to ease application development. Our stated
Mark Slee24b49d32007-03-21 01:24:00 +0000722goal is not to make developers learn a rich new library in their language of
723choice, but rather to generate code that allow them to work with the constructs
724that are most familiar in each language.
725
726We also made the \texttt{read()} and \texttt{write()} methods of the generated
Mark Sleeafaf2762007-04-03 19:47:04 +0000727objects public so that the objects can be used outside of the context
Mark Slee24b49d32007-03-21 01:24:00 +0000728of RPC clients and servers. Thrift is a useful tool simply for generating
729objects that are easily serializable across programming languages.
730
731\subsection{RPC Method Identification}
732Method calls in RPC are implemented by sending the method name as a string. One
733issue with this approach is that longer method names require more bandwidth.
734We experimented with using fixed-size hashes to identify methods, but in the
735end concluded that the savings were not worth the headaches incurred. Reliably
736dealing with conflicts across versions of an interface definition file is
737impossible without a meta-storage system (i.e. to generate non-conflicting
738hashes for the current version of a file, we would have to know about all
739conflicts that ever existed in any previous version of the file).
740
741We wanted to avoid too many unnecessary string comparisons upon
742method invocation. To deal with this, we generate maps from strings to function
743pointers, so that invocation is effectively accomplished via a constant-time
744hash lookup in the common case. This requires the use of a couple interesting
745code constructs. Because Java does not have function pointers, process
746functions are all private member classes implementing a common interface.
747
748\begin{verbatim}
749private class ping implements ProcessFunction {
750 public void process(int seqid,
751 TProtocol iprot,
752 TProtocol oprot)
753 throws TException
754 { ...}
755}
756
757HashMap<String,ProcessFunction> processMap_ =
758 new HashMap<String,ProcessFunction>();
759\end{verbatim}
760
761In C++, we use a relatively esoteric language construct: member function
762pointers.
763
764\begin{verbatim}
765std::map<std::string,
766 void (ExampleServiceProcessor::*)(int32_t,
767 facebook::thrift::protocol::TProtocol*,
768 facebook::thrift::protocol::TProtocol*)>
769 processMap_;
770\end{verbatim}
771
772Using these techniques, the cost of string processing is minimized, and we
773reap the benefit of being able to easily debug corrupt or misunderstood data by
Mark Sleeafaf2762007-04-03 19:47:04 +0000774inspecting it for known string method names.
Mark Slee24b49d32007-03-21 01:24:00 +0000775
776\subsection{Servers and Multithreading}
Mark Sleeafaf2762007-04-03 19:47:04 +0000777Thrift services require basic multithreading to handle simultaneous
Mark Sleec11045a2007-04-01 23:14:38 +0000778requests from multiple clients. For the Python and Java implementations of
Mark Sleeafaf2762007-04-03 19:47:04 +0000779Thrift server logic, the standard threading libraries distributed with the
780languages provide adequate support. For the C++ implementation, no standard multithread runtime
David Reiss0c90f6f2008-02-06 22:18:40 +0000781library exists. Specifically, robust, lightweight, and portable
Mark Sleeafaf2762007-04-03 19:47:04 +0000782thread manager and timer class implementations do not exist. We investigated
David Reiss0c90f6f2008-02-06 22:18:40 +0000783existing implementations, namely \texttt{boost::thread},
Mark Sleec11045a2007-04-01 23:14:38 +0000784\texttt{boost::threadpool}, \texttt{ACE\_Thread\_Manager} and
David Reiss0c90f6f2008-02-06 22:18:40 +0000785\texttt{ACE\_Timer}.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000786
Mark Sleec11045a2007-04-01 23:14:38 +0000787While \texttt{boost::threads}\cite{boost.threads} provides clean,
788lightweight and robust implementations of multi-thread primitives (mutexes,
789conditions, threads) it does not provide a thread manager or timer
David Reiss0c90f6f2008-02-06 22:18:40 +0000790implementation.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000791
Mark Sleec11045a2007-04-01 23:14:38 +0000792\texttt{boost::threadpool}\cite{boost.threadpool} also looked promising but
793was not far enough along for our purposes. We wanted to limit the dependency on
Mark Sleeafaf2762007-04-03 19:47:04 +0000794third-party libraries as much as possible. Because\\
Mark Sleec11045a2007-04-01 23:14:38 +0000795\texttt{boost::threadpool} is
796not a pure template library and requires runtime libraries and because it is
Mark Sleeafaf2762007-04-03 19:47:04 +0000797not yet part of the official Boost distribution we felt it was not ready for
Mark Sleec11045a2007-04-01 23:14:38 +0000798use in Thrift. As \texttt{boost::threadpool} evolves and especially if it is
Mark Sleeafaf2762007-04-03 19:47:04 +0000799added to the Boost distribution we may reconsider our decision to not use it.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000800
801ACE has both a thread manager and timer class in addition to multi-thread
Mark Sleeafaf2762007-04-03 19:47:04 +0000802primitives. The biggest problem with ACE is that it is ACE. Unlike Boost, ACE
Mark Sleec11045a2007-04-01 23:14:38 +0000803API quality is poor. Everything in ACE has large numbers of dependencies on
David Reiss0c90f6f2008-02-06 22:18:40 +0000804everything else in ACE - thus forcing developers to throw out standard
Mark Sleeafaf2762007-04-03 19:47:04 +0000805classes, such as STL collections, in favor of ACE's homebrewed implementations. In
806addition, unlike Boost, ACE implementations demonstrate little understanding
Mark Sleec11045a2007-04-01 23:14:38 +0000807of the power and pitfalls of C++ programming and take no advantage of modern
808templating techniques to ensure compile time safety and reasonable compiler
Mark Sleeafaf2762007-04-03 19:47:04 +0000809error messages. For all these reasons, ACE was rejected. Instead, we chose
810to implement our own library, described in the following sections.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000811
812\subsection{Thread Primitives}
813
Mark Sleec11045a2007-04-01 23:14:38 +0000814The Thrift thread libraries are implemented in the namespace\\
815\texttt{facebook::thrift::concurrency} and have three components:
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000816\begin{itemize}
Mark Sleec11045a2007-04-01 23:14:38 +0000817\item primitives
818\item thread pool manager
819\item timer manager
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000820\end{itemize}
821
Mark Sleec11045a2007-04-01 23:14:38 +0000822As mentioned above, we were hesitant to introduce any additional dependencies
David Reiss0c90f6f2008-02-06 22:18:40 +0000823on Thrift. We decided to use \texttt{boost::shared\_ptr} because it is so
Mark Sleeafaf2762007-04-03 19:47:04 +0000824useful for multithreaded application, it requires no link-time or
825runtime libraries (i.e. it is a pure template library) and it is due
826to become part of the C++0x standard.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000827
Mark Sleec11045a2007-04-01 23:14:38 +0000828We implement standard \texttt{Mutex} and \texttt{Condition} classes, and a
David Reiss0c90f6f2008-02-06 22:18:40 +0000829 \texttt{Monitor} class. The latter is simply a combination of a mutex and
Mark Sleeafaf2762007-04-03 19:47:04 +0000830condition variable and is analogous to the \texttt{Monitor} implementation provided for
David Reiss0c90f6f2008-02-06 22:18:40 +0000831the Java \texttt{Object} class. This is also sometimes referred to as a barrier. We
Mark Sleec11045a2007-04-01 23:14:38 +0000832provide a \texttt{Synchronized} guard class to allow Java-like synchronized blocks.
David Reiss0c90f6f2008-02-06 22:18:40 +0000833This is just a bit of syntactic sugar, but, like its Java counterpart, clearly
Mark Sleeafaf2762007-04-03 19:47:04 +0000834delimits critical sections of code. Unlike its Java counterpart, we still
Mark Sleec11045a2007-04-01 23:14:38 +0000835have the ability to programmatically lock, unlock, block, and signal monitors.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000836
837\begin{verbatim}
Mark Sleec11045a2007-04-01 23:14:38 +0000838void run() {
839 {Synchronized s(manager->monitor);
840 if (manager->state == TimerManager::STARTING) {
841 manager->state = TimerManager::STARTED;
842 manager->monitor.notifyAll();
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000843 }
Mark Sleec11045a2007-04-01 23:14:38 +0000844 }
845}
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000846\end{verbatim}
847
Mark Sleec11045a2007-04-01 23:14:38 +0000848We again borrowed from Java the distinction between a thread and a runnable
849class. A \texttt{Thread} is the actual schedulable object. The
David Reiss0c90f6f2008-02-06 22:18:40 +0000850\texttt{Runnable} is the logic to execute within the thread.
851The \texttt{Thread} implementation deals with all the platform-specific thread
Mark Sleeafaf2762007-04-03 19:47:04 +0000852creation and destruction issues, while the \texttt{Runnable} implementation deals
Mark Sleec11045a2007-04-01 23:14:38 +0000853with the application-specific per-thread logic. The benefit of this approach
David Reiss0c90f6f2008-02-06 22:18:40 +0000854is that developers can easily subclass the Runnable class without pulling in
Aditya Agarwal8c5daa42007-04-05 01:17:52 +0000855platform-specific super-classes.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000856
857\subsection{Thread, Runnable, and shared\_ptr}
Mark Sleec11045a2007-04-01 23:14:38 +0000858We use \texttt{boost::shared\_ptr} throughout the \texttt{ThreadManager} and
859\texttt{TimerManager} implementations to guarantee cleanup of dead objects that can
860be accessed by multiple threads. For \texttt{Thread} class implementations,
861\texttt{boost::shared\_ptr} usage requires particular attention to make sure
862\texttt{Thread} objects are neither leaked nor dereferenced prematurely while
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000863creating and shutting down threads.
864
Mark Sleec11045a2007-04-01 23:14:38 +0000865Thread creation requires calling into a C library. (In our case the POSIX
Mark Slee09bfd612007-04-04 19:56:41 +0000866thread library, \texttt{libpthread}, but the same would be true for WIN32 threads).
Mark Sleeafaf2762007-04-03 19:47:04 +0000867Typically, the OS makes few, if any, guarantees about when \texttt{ThreadMain}, a C thread's entry-point function, will be called. Therefore, it is
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000868possible that our thread create call,
Mark Sleec11045a2007-04-01 23:14:38 +0000869\texttt{ThreadFactory::newThread()} could return to the caller
870well before that time. To ensure that the returned \texttt{Thread} object is not
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000871prematurely cleaned up if the caller gives up its reference prior to the
Mark Sleec11045a2007-04-01 23:14:38 +0000872\texttt{ThreadMain} call, the \texttt{Thread} object makes a weak referenence to
873itself in its \texttt{start} method.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000874
Mark Sleec11045a2007-04-01 23:14:38 +0000875With the weak reference in hand the \texttt{ThreadMain} function can attempt to get
876a strong reference before entering the \texttt{Runnable::run} method of the
Mark Sleeafaf2762007-04-03 19:47:04 +0000877\texttt{Runnable} object bound to the \texttt{Thread}. If no strong references to the
David Reiss0c90f6f2008-02-06 22:18:40 +0000878thread are obtained between exiting \texttt{Thread::start} and entering \texttt{ThreadMain}, the weak reference returns \texttt{null} and the function
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000879exits immediately.
880
Mark Sleec11045a2007-04-01 23:14:38 +0000881The need for the \texttt{Thread} to make a weak reference to itself has a
882significant impact on the API. Since references are managed through the
883\texttt{boost::shared\_ptr} templates, the \texttt{Thread} object must have a reference
884to itself wrapped by the same \texttt{boost::shared\_ptr} envelope that is returned
Mark Sleeafaf2762007-04-03 19:47:04 +0000885to the caller. This necessitated the use of the factory pattern.
886\texttt{ThreadFactory} creates the raw \texttt{Thread} object and a
887\texttt{boost::shared\_ptr} wrapper, and calls a private helper method of the class
888implementing the \texttt{Thread} interface (in this case, \texttt{PosixThread::weakRef})
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000889 to allow it to make add weak reference to itself through the
Mark Sleec11045a2007-04-01 23:14:38 +0000890 \texttt{boost::shared\_ptr} envelope.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000891
Mark Sleec11045a2007-04-01 23:14:38 +0000892\texttt{Thread} and \texttt{Runnable} objects reference each other. A \texttt{Runnable}
Mark Sleeafaf2762007-04-03 19:47:04 +0000893object may need to know about the thread in which it is executing, and a Thread, obviously,
Mark Sleec11045a2007-04-01 23:14:38 +0000894needs to know what \texttt{Runnable} object it is hosting. This interdependency is
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000895further complicated because the lifecycle of each object is independent of the
Mark Slee8142b9d2007-04-03 05:49:12 +0000896other. An application may create a set of \texttt{Runnable} object to be reused in different threads, or it may create and forget a \texttt{Runnable} object
David Reiss0c90f6f2008-02-06 22:18:40 +0000897once a thread has been created and started for it.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000898
Mark Sleec11045a2007-04-01 23:14:38 +0000899The \texttt{Thread} class takes a \texttt{boost::shared\_ptr} reference to the hosted
Mark Sleeafaf2762007-04-03 19:47:04 +0000900\texttt{Runnable} object in its constructor, while the \texttt{Runnable} class has an
Mark Sleec11045a2007-04-01 23:14:38 +0000901explicit \texttt{thread} method to allow explicit binding of the hosted thread.
Mark Sleeafaf2762007-04-03 19:47:04 +0000902\texttt{ThreadFactory::newThread} binds the objects to each other.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000903
904\subsection{ThreadManager}
905
David Reiss0c90f6f2008-02-06 22:18:40 +0000906\texttt{ThreadManager} creates a pool of worker threads and
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000907allows applications to schedule tasks for execution as free worker threads
David Reiss0c90f6f2008-02-06 22:18:40 +0000908become available. The \texttt{ThreadManager} does not implement dynamic
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000909thread pool resizing, but provides primitives so that applications can add
David Reiss0c90f6f2008-02-06 22:18:40 +0000910and remove threads based on load. This approach was chosen because
911implementing load metrics and thread pool size is very application
Mark Sleec11045a2007-04-01 23:14:38 +0000912specific. For example some applications may want to adjust pool size based
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000913on running-average of work arrival rates that are measured via polled
Mark Sleec11045a2007-04-01 23:14:38 +0000914samples. Others may simply wish to react immediately to work-queue
915depth high and low water marks. Rather than trying to create a complex
David Reiss0c90f6f2008-02-06 22:18:40 +0000916API abstract enough to capture these different approaches, we
917simply leave it up to the particular application and provide the
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000918primitives to enact the desired policy and sample current status.
919
920\subsection{TimerManager}
921
Mark Sleeafaf2762007-04-03 19:47:04 +0000922\texttt{TimerManager} allows applications to schedule
David Reiss0c90f6f2008-02-06 22:18:40 +0000923 \texttt{Runnable} objects for execution at some point in the future. Its specific task
Mark Sleec11045a2007-04-01 23:14:38 +0000924is to allows applications to sample \texttt{ThreadManager} load at regular
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000925intervals and make changes to the thread pool size based on application policy.
Mark Sleec11045a2007-04-01 23:14:38 +0000926Of course, it can be used to generate any number of timer or alarm events.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000927
Mark Sleec11045a2007-04-01 23:14:38 +0000928The default implementation of \texttt{TimerManager} uses a single thread to
David Reiss0c90f6f2008-02-06 22:18:40 +0000929execute expired \texttt{Runnable} objects. Thus, if a timer operation needs to
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000930do a large amount of work and especially if it needs to do blocking I/O,
931that should be done in a separate thread.
Mark Slee24b49d32007-03-21 01:24:00 +0000932
933\subsection{Nonblocking Operation}
934Though the Thrift transport interfaces map more directly to a blocking I/O
935model, we have implemented a high performance \texttt{TNonBlockingServer}
Mark Sleeafaf2762007-04-03 19:47:04 +0000936in C++ based on \texttt{libevent} and the \texttt{TFramedTransport}. We
Mark Slee24b49d32007-03-21 01:24:00 +0000937implemented this by moving all I/O into one tight event loop using a
938state machine. Essentially, the event loop reads framed requests into
939\texttt{TMemoryBuffer} objects. Once entire requests are ready, they are
940dispatched to the \texttt{TProcessor} object which can read directly from
941the data in memory.
942
943\subsection{Compiler}
Mark Sleeafaf2762007-04-03 19:47:04 +0000944The Thrift compiler is implemented in C++ using standard \texttt{lex}/\texttt{yacc}
945lexing and parsing. Though it could have been implemented with fewer
946lines of code in another language (i.e. Python Lex-Yacc (PLY) or \texttt{ocamlyacc}), using C++
Mark Slee24b49d32007-03-21 01:24:00 +0000947forces explicit definition of the language constructs. Strongly typing the
948parse tree elements (debatably) makes the code more approachable for new
949developers.
950
951Code generation is done using two passes. The first pass looks only for
952include files and type definitions. Type definitions are not checked during
953this phase, since they may depend upon include files. All included files
954are sequentially scanned in a first pass. Once the include tree has been
Mark Sleeafaf2762007-04-03 19:47:04 +0000955resolved, a second pass over all files is taken that inserts type definitions
Mark Slee24b49d32007-03-21 01:24:00 +0000956into the parse tree and raises an error on any undefined types. The program is
957then generated against the parse tree.
958
959Due to inherent complexities and potential for circular dependencies,
960we explicitly disallow forward declaration. Two Thrift structs cannot
961each contain an instance of the other. (Since we do not allow \texttt{null}
962struct instances in the generated C++ code, this would actually be impossible.)
963
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000964\subsection{TFileTransport}
David Reiss0c90f6f2008-02-06 22:18:40 +0000965The \texttt{TFileTransport} logs Thrift requests/structs by
966framing incoming data with its length and writing it out to disk.
967Using a framed on-disk format allows for better error checking and
Mark Sleeafaf2762007-04-03 19:47:04 +0000968helps with the processing of a finite number of discrete events. The\\
David Reiss0c90f6f2008-02-06 22:18:40 +0000969\texttt{TFileWriterTransport} uses a system of swapping in-memory buffers
970to ensure good performance while logging large amounts of data.
Mark Sleeafaf2762007-04-03 19:47:04 +0000971A Thrift log file is split up into chunks of a specified size; logged messages
David Reiss0c90f6f2008-02-06 22:18:40 +0000972are not allowed to cross chunk boundaries. A message that would cross a chunk
973boundary will cause padding to be added until the end of the chunk and the
Mark Sleeafaf2762007-04-03 19:47:04 +0000974first byte of the message are aligned to the beginning of the next chunk.
David Reiss0c90f6f2008-02-06 22:18:40 +0000975Partitioning the file into chunks makes it possible to read and interpret data
976from a particular point in the file.
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000977
Mark Sleec11045a2007-04-01 23:14:38 +0000978\section{Facebook Thrift Services}
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000979Thrift has been employed in a large number of applications at Facebook, including
Mark Sleeafaf2762007-04-03 19:47:04 +0000980search, logging, mobile, ads and the developer platform. Two specific usages are discussed below.
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000981
982\subsection{Search}
Mark Sleeafaf2762007-04-03 19:47:04 +0000983Thrift is used as the underlying protocol and transport layer for the Facebook Search service.
984The multi-language code generation is well suited for search because it allows for application
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000985development in an efficient server side language (C++) and allows the Facebook PHP-based web application
986to make calls to the search service using Thrift PHP libraries. There is also a large
David Reiss0c90f6f2008-02-06 22:18:40 +0000987variety of search stats, deployment and testing functionality that is built on top
Mark Sleeafaf2762007-04-03 19:47:04 +0000988of generated Python code. Additionally, the Thrift log file format is
David Reiss0c90f6f2008-02-06 22:18:40 +0000989used as a redo log for providing real-time search index updates. Thrift has allowed the
990search team to leverage each language for its strengths and to develop code at a rapid pace.
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000991
992\subsection{Logging}
993The Thrift \texttt{TFileTransport} functionality is used for structured logging. Each
994service function definition along with its parameters can be considered to be
David Reiss0c90f6f2008-02-06 22:18:40 +0000995a structured log entry identified by the function name. This log can then be used for
Mark Sleeafaf2762007-04-03 19:47:04 +0000996a variety of purposes, including inline and offline processing, stats aggregation and as a redo log.
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000997
Mark Slee24b49d32007-03-21 01:24:00 +0000998\section{Conclusions}
999Thrift has enabled Facebook to build scalable backend
1000services efficiently by enabling engineers to divide and conquer. Application
Mark Sleeafaf2762007-04-03 19:47:04 +00001001developers can focus on application code without worrying about the
Mark Slee24b49d32007-03-21 01:24:00 +00001002sockets layer. We avoid duplicated work by writing buffering and I/O logic
1003in one place, rather than interspersing it in each application.
1004
1005Thrift has been employed in a wide variety of applications at Facebook,
Mark Sleeafaf2762007-04-03 19:47:04 +00001006including search, logging, mobile, ads, and the developer platform. We have
Mark Slee24b49d32007-03-21 01:24:00 +00001007found that the marginal performance cost incurred by an extra layer of
Mark Sleeafaf2762007-04-03 19:47:04 +00001008software abstraction is far eclipsed by the gains in developer efficiency and
Mark Slee24b49d32007-03-21 01:24:00 +00001009systems reliability.
1010
1011\appendix
1012
1013\section{Similar Systems}
1014The following are software systems similar to Thrift. Each is (very!) briefly
1015described:
1016
1017\begin{itemize}
1018\item \textit{SOAP.} XML-based. Designed for web services via HTTP, excessive
1019XML parsing overhead.
1020\item \textit{CORBA.} Relatively comprehensive, debatably overdesigned and
1021heavyweight. Comparably cumbersome software installation.
1022\item \textit{COM.} Embraced mainly in Windows client softare. Not an entirely
1023open solution.
1024\item \textit{Pillar.} Lightweight and high-performance, but missing versioning
1025and abstraction.
1026\item \textit{Protocol Buffers.} Closed-source, owned by Google. Described in
1027Sawzall paper.
1028\end{itemize}
1029
1030\acks
1031
1032Many thanks for feedback on Thrift (and extreme trial by fire) are due to
Aditya Agarwalaf524ee2007-03-31 08:28:06 +00001033Martin Smith, Karl Voskuil and Yishan Wong.
Mark Slee24b49d32007-03-21 01:24:00 +00001034
1035Thrift is a successor to Pillar, a similar system developed
1036by Adam D'Angelo, first while at Caltech and continued later at Facebook.
1037Thrift simply would not have happened without Adam's insights.
1038
Marc Slemko10b3bdb2007-04-01 09:14:05 +00001039\begin{thebibliography}{}
Mark Slee24b49d32007-03-21 01:24:00 +00001040
Marc Slemko10b3bdb2007-04-01 09:14:05 +00001041\bibitem{boost.threads}
1042Kempf, William,
1043``Boost.Threads'',
1044\url{http://www.boost.org/doc/html/threads.html}
Mark Slee24b49d32007-03-21 01:24:00 +00001045
Marc Slemko10b3bdb2007-04-01 09:14:05 +00001046\bibitem{boost.threadpool}
1047Henkel, Philipp,
1048``threadpool'',
1049\url{http://threadpool.sourceforge.net}
1050
1051\end{thebibliography}
Mark Slee24b49d32007-03-21 01:24:00 +00001052
1053\end{document}