blob: a706fcbbcddfafa46f45c1997a58d73bf652164a [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%
David Reisse9aab102008-04-03 20:16:53 +000011% You will need a copy of sigplanconf.cls to format this document.
12% It is available at <http://www.sigplan.org/authorInformation.htm>.
13%
Mark Slee24b49d32007-03-21 01:24:00 +000014%-----------------------------------------------------------------------------
15
16
17\documentclass[nocopyrightspace,blockstyle]{sigplanconf}
18
19\usepackage{amssymb}
20\usepackage{amsfonts}
21\usepackage{amsmath}
Marc Slemko10b3bdb2007-04-01 09:14:05 +000022\usepackage{url}
Mark Slee24b49d32007-03-21 01:24:00 +000023
24\begin{document}
25
David Reiss0c90f6f2008-02-06 22:18:40 +000026% \conferenceinfo{WXYZ '05}{date, City.}
Mark Slee24b49d32007-03-21 01:24:00 +000027% \copyrightyear{2007}
David Reiss0c90f6f2008-02-06 22:18:40 +000028% \copyrightdata{[to be supplied]}
Mark Slee24b49d32007-03-21 01:24:00 +000029
30% \titlebanner{banner above paper title} % These are ignored unless
31% \preprintfooter{short description of paper} % 'preprint' option specified.
32
33\title{Thrift: Scalable Cross-Language Services Implementation}
34\subtitle{}
35
36\authorinfo{Mark Slee, Aditya Agarwal and Marc Kwiatkowski}
37 {Facebook, 156 University Ave, Palo Alto, CA}
38 {\{mcslee,aditya,marc\}@facebook.com}
39
40\maketitle
41
42\begin{abstract}
43Thrift is a software library and set of code-generation tools developed at
44Facebook to expedite development and implementation of efficient and scalable
45backend services. Its primary goal is to enable efficient and reliable
46communication across programming languages by abstracting the portions of each
47language that tend to require the most customization into a common library
48that is implemented in each language. Specifically, Thrift allows developers to
Mark Sleeafaf2762007-04-03 19:47:04 +000049define datatypes and service interfaces in a single language-neutral file
Mark Slee24b49d32007-03-21 01:24:00 +000050and generate all the necessary code to build RPC clients and servers.
51
52This paper details the motivations and design choices we made in Thrift, as
53well as some of the more interesting implementation details. It is not
54intended to be taken as research, but rather it is an exposition on what we did
55and why.
56\end{abstract}
57
58% \category{D.3.3}{Programming Languages}{Language constructs and features}
59
60%\terms
61%Languages, serialization, remote procedure call
62
63%\keywords
64%Data description language, interface definition language, remote procedure call
65
66\section{Introduction}
67As Facebook's traffic and network structure have scaled, the resource
David Reiss0c90f6f2008-02-06 22:18:40 +000068demands of many operations on the site (i.e. search,
Mark Slee24b49d32007-03-21 01:24:00 +000069ad selection and delivery, event logging) have presented technical requirements
70drastically outside the scope of the LAMP framework. In our implementation of
71these services, various programming languages have been selected to
72optimize for the right combination of performance, ease and speed of
73development, availability of existing libraries, etc. By and large,
74Facebook's engineering culture has tended towards choosing the best
Mark Sleeafaf2762007-04-03 19:47:04 +000075tools and implementations available over standardizing on any one
Mark Slee24b49d32007-03-21 01:24:00 +000076programming language and begrudgingly accepting its inherent limitations.
77
78Given this design choice, we were presented with the challenge of building
79a transparent, high-performance bridge across many programming languages.
80We found that most available solutions were either too limited, did not offer
Mark Sleeafaf2762007-04-03 19:47:04 +000081sufficient datatype freedom, or suffered from subpar performance.
Mark Slee24b49d32007-03-21 01:24:00 +000082\footnote{See Appendix A for a discussion of alternative systems.}
83
84The solution that we have implemented combines a language-neutral software
85stack implemented across numerous programming languages and an associated code
86generation engine that transforms a simple interface and data definition
87language into client and server remote procedure call libraries.
88Choosing static code generation over a dynamic system allows us to create
Mark Sleeafaf2762007-04-03 19:47:04 +000089validated code that can be run without the need for
90any advanced introspective run-time type checking. It is also designed to
Mark Slee24b49d32007-03-21 01:24:00 +000091be as simple as possible for the developer, who can typically define all
92the necessary data structures and interfaces for a complex service in a single
93short file.
94
95Surprised that a robust open solution to these relatively common problems
96did not yet exist, we committed early on to making the Thrift implementation
97open source.
98
99In evaluating the challenges of cross-language interaction in a networked
100environment, some key components were identified:
101
102\textit{Types.} A common type system must exist across programming languages
Mark Sleeafaf2762007-04-03 19:47:04 +0000103without requiring that the application developer use custom Thrift datatypes
Mark Slee24b49d32007-03-21 01:24:00 +0000104or write their own serialization code. That is,
105a C++ programmer should be able to transparently exchange a strongly typed
106STL map for a dynamic Python dictionary. Neither
107programmer should be forced to write any code below the application layer
David Reiss0c90f6f2008-02-06 22:18:40 +0000108to achieve this. Section 2 details the Thrift type system.
Mark Slee24b49d32007-03-21 01:24:00 +0000109
110\textit{Transport.} Each language must have a common interface to
111bidirectional raw data transport. The specifics of how a given
112transport is implemented should not matter to the service developer.
113The same application code should be able to run against TCP stream sockets,
114raw data in memory, or files on disk. Section 3 details the Thrift Transport
115layer.
116
Mark Sleeafaf2762007-04-03 19:47:04 +0000117\textit{Protocol.} Datatypes must have some way of using the Transport
Mark Slee24b49d32007-03-21 01:24:00 +0000118layer to encode and decode themselves. Again, the application
119developer need not be concerned by this layer. Whether the service uses
120an XML or binary protocol is immaterial to the application code.
121All that matters is that the data can be read and written in a consistent,
122deterministic matter. Section 4 details the Thrift Protocol layer.
123
Mark Sleeafaf2762007-04-03 19:47:04 +0000124\textit{Versioning.} For robust services, the involved datatypes must
Mark Slee24b49d32007-03-21 01:24:00 +0000125provide a mechanism for versioning themselves. Specifically,
126it should be possible to add or remove fields in an object or alter the
127argument list of a function without any interruption in service (or,
128worse yet, nasty segmentation faults). Section 5 details Thrift's versioning
129system.
130
131\textit{Processors.} Finally, we generate code capable of processing data
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000132streams to accomplish remote procedure calls. Section 6 details the generated
Mark Slee24b49d32007-03-21 01:24:00 +0000133code and TProcessor paradigm.
134
135Section 7 discusses implementation details, and Section 8 describes
136our conclusions.
137
138\section{Types}
139
140The goal of the Thrift type system is to enable programmers to develop using
141completely natively defined types, no matter what programming language they
142use. By design, the Thrift type system does not introduce any special dynamic
143types or wrapper objects. It also does not require that the developer write
Mark Sleeafaf2762007-04-03 19:47:04 +0000144any code for object serialization or transport. The Thrift IDL (Interface
145Definition Language) file is
Mark Slee24b49d32007-03-21 01:24:00 +0000146logically a way for developers to annotate their data structures with the
147minimal amount of extra information necessary to tell a code generator
148how to safely transport the objects across languages.
149
150\subsection{Base Types}
151
152The type system rests upon a few base types. In considering which types to
153support, we aimed for clarity and simplicity over abundance, focusing
Roger Meier3f067a82011-03-04 13:35:05 +0000154on the key types available in all programming languages, omitting any
Mark Slee24b49d32007-03-21 01:24:00 +0000155niche types available only in specific languages.
156
157The base types supported by Thrift are:
158\begin{itemize}
159\item \texttt{bool} A boolean value, true or false
160\item \texttt{byte} A signed byte
161\item \texttt{i16} A 16-bit signed integer
162\item \texttt{i32} A 32-bit signed integer
163\item \texttt{i64} A 64-bit signed integer
164\item \texttt{double} A 64-bit floating point number
165\item \texttt{string} An encoding-agnostic text or binary string
Bryan Duxbury7003f872009-02-01 06:21:13 +0000166\item \texttt{binary} A byte array representation for blobs
Mark Slee24b49d32007-03-21 01:24:00 +0000167\end{itemize}
168
169Of particular note is the absence of unsigned integer types. Because these
170types have no direct translation to native primitive types in many languages,
171the advantages they afford are lost. Further, there is no way to prevent the
172application developer in a language like Python from assigning a negative value
173to an integer variable, leading to unpredictable behavior. From a design
174standpoint, we observed that unsigned integers were very rarely, if ever, used
175for arithmetic purposes, but in practice were much more often used as keys or
176identifiers. In this case, the sign is irrelevant. Signed integers serve this
177same purpose and can be safely cast to their unsigned counterparts (most
178commonly in C++) when absolutely necessary.
179
Mark Slee24b49d32007-03-21 01:24:00 +0000180\subsection{Structs}
181
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000182A Thrift struct defines a common object to be used across languages. A struct
Mark Slee24b49d32007-03-21 01:24:00 +0000183is essentially equivalent to a class in object oriented programming
184languages. A struct has a set of strongly typed fields, each with a unique
185name identifier. The basic syntax for defining a Thrift struct looks very
186similar to a C struct definition. Fields may be annotated with an integer field
187identifier (unique to the scope of that struct) and optional default values.
188Field identifiers will be automatically assigned if omitted, though they are
189strongly encouraged for versioning reasons discussed later.
190
Mark Sleeafaf2762007-04-03 19:47:04 +0000191\subsection{Containers}
192
193Thrift containers are strongly typed containers that map to the most commonly
194used containers in common programming languages. They are annotated using
195the C++ template (or Java Generics) style. There are three types available:
196\begin{itemize}
197\item \texttt{list<type>} An ordered list of elements. Translates directly into
198an STL \texttt{vector}, Java \texttt{ArrayList}, or native array in scripting languages. May
199contain duplicates.
200\item \texttt{set<type>} An unordered set of unique elements. Translates into
201an STL \texttt{set}, Java \texttt{HashSet}, \texttt{set} in Python, or native
David Reiss0c90f6f2008-02-06 22:18:40 +0000202dictionary in PHP/Ruby.
Mark Sleeafaf2762007-04-03 19:47:04 +0000203\item \texttt{map<type1,type2>} A map of strictly unique keys to values
Mark Slee09bfd612007-04-04 19:56:41 +0000204Translates into an STL \texttt{map}, Java \texttt{HashMap}, PHP associative
Mark Sleeafaf2762007-04-03 19:47:04 +0000205array, or Python/Ruby dictionary.
206\end{itemize}
207
208While defaults are provided, the type mappings are not explicitly fixed. Custom
209code generator directives have been added to substitute custom types in
210destination languages (i.e.
211\texttt{hash\_map} or Google's sparse hash map can be used in C++). The
212only requirement is that the custom types support all the necessary iteration
213primitives. Container elements may be of any valid Thrift type, including other
214containers or structs.
215
Mark Slee24b49d32007-03-21 01:24:00 +0000216\begin{verbatim}
217struct Example {
218 1:i32 number=10,
219 2:i64 bigNumber,
220 3:double decimals,
221 4:string name="thrifty"
222}\end{verbatim}
223
224In the target language, each definition generates a type with two methods,
225\texttt{read} and \texttt{write}, which perform serialization and transport
226of the objects using a Thrift TProtocol object.
227
228\subsection{Exceptions}
229
230Exceptions are syntactically and functionally equivalent to structs except
231that they are declared using the \texttt{exception} keyword instead of the
232\texttt{struct} keyword.
233
234The generated objects inherit from an exception base class as appropriate
Mark Sleeafaf2762007-04-03 19:47:04 +0000235in each target programming language, in order to seamlessly
236integrate with native exception handling in any given
Mark Slee24b49d32007-03-21 01:24:00 +0000237language. Again, the design emphasis is on making the code familiar to the
238application developer.
239
240\subsection{Services}
241
242Services are defined using Thrift types. Definition of a service is
Mark Sleeafaf2762007-04-03 19:47:04 +0000243semantically equivalent to defining an interface (or a pure virtual abstract
244class) in object oriented
Mark Slee24b49d32007-03-21 01:24:00 +0000245programming. The Thrift compiler generates fully functional client and
246server stubs that implement the interface. Services are defined as follows:
247
248\begin{verbatim}
249service <name> {
250 <returntype> <name>(<arguments>)
251 [throws (<exceptions>)]
252 ...
253}\end{verbatim}
254
255An example:
256
257\begin{verbatim}
258service StringCache {
259 void set(1:i32 key, 2:string value),
260 string get(1:i32 key) throws (1:KeyNotFound knf),
David Reiss0c90f6f2008-02-06 22:18:40 +0000261 void delete(1:i32 key)
Mark Slee24b49d32007-03-21 01:24:00 +0000262}
263\end{verbatim}
264
265Note that \texttt{void} is a valid type for a function return, in addition to
266all other defined Thrift types. Additionally, an \texttt{async} modifier
Mark Sleeafaf2762007-04-03 19:47:04 +0000267keyword may be added to a \texttt{void} function, which will generate code that does
Mark Slee24b49d32007-03-21 01:24:00 +0000268not wait for a response from the server. Note that a pure \texttt{void}
269function will return a response to the client which guarantees that the
270operation has completed on the server side. With \texttt{async} method calls
Mark Sleeafaf2762007-04-03 19:47:04 +0000271the client will only be guaranteed that the request succeeded at the
Mark Slee24b49d32007-03-21 01:24:00 +0000272transport layer. (In many transport scenarios this is inherently unreliable
273due to the Byzantine Generals' Problem. Therefore, application developers
Mark Sleeafaf2762007-04-03 19:47:04 +0000274should take care only to use the async optimization in cases where dropped
Mark Slee24b49d32007-03-21 01:24:00 +0000275method calls are acceptable or the transport is known to be reliable.)
276
Mark Sleeafaf2762007-04-03 19:47:04 +0000277Also of note is the fact that argument lists and exception lists for functions
278are implemented as Thrift structs. All three constructs are identical in both
279notation and behavior.
Mark Slee24b49d32007-03-21 01:24:00 +0000280
281\section{Transport}
282
283The transport layer is used by the generated code to facilitate data transfer.
284
285\subsection{Interface}
286
Mark Sleeafaf2762007-04-03 19:47:04 +0000287A key design choice in the implementation of Thrift was to decouple the
Mark Slee24b49d32007-03-21 01:24:00 +0000288transport layer from the code generation layer. Though Thrift is typically
289used on top of the TCP/IP stack with streaming sockets as the base layer of
David Reiss0c90f6f2008-02-06 22:18:40 +0000290communication, there was no compelling reason to build that constraint into
Mark Slee24b49d32007-03-21 01:24:00 +0000291the system. The performance tradeoff incurred by an abstracted I/O layer
292(roughly one virtual method lookup / function call per operation) was
293immaterial compared to the cost of actual I/O operations (typically invoking
294system calls).
295
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000296Fundamentally, generated Thrift code only needs to know how to read and
Mark Sleeafaf2762007-04-03 19:47:04 +0000297write data. The origin and destination of the data are irrelevant; it may be a
298socket, a segment of shared memory, or a file on the local disk. The Thrift
299transport interface supports the following methods:
Mark Slee24b49d32007-03-21 01:24:00 +0000300
301\begin{itemize}
Roger Meier3f067a82011-03-04 13:35:05 +0000302\item \texttt{open} Opens the transport
303\item \texttt{close} Closes the transport
Mark Sleeafaf2762007-04-03 19:47:04 +0000304\item \texttt{isOpen} Indicates whether the transport is open
305\item \texttt{read} Reads from the transport
306\item \texttt{write} Writes to the transport
307\item \texttt{flush} Forces any pending writes
Mark Slee24b49d32007-03-21 01:24:00 +0000308\end{itemize}
309
310There are a few additional methods not documented here which are used to aid
Mark Sleeafaf2762007-04-03 19:47:04 +0000311in batching reads and optionally signaling the completion of a read or
312write operation from the generated code.
Mark Slee24b49d32007-03-21 01:24:00 +0000313
314In addition to the above
Mark Sleec11045a2007-04-01 23:14:38 +0000315\texttt{TTransport} interface, there is a\\
316\texttt{TServerTransport} interface
Mark Slee24b49d32007-03-21 01:24:00 +0000317used to accept or create primitive transport objects. Its interface is as
318follows:
319
320\begin{itemize}
Mark Sleeafaf2762007-04-03 19:47:04 +0000321\item \texttt{open} Opens the transport
322\item \texttt{listen} Begins listening for connections
323\item \texttt{accept} Returns a new client transport
324\item \texttt{close} Closes the transport
Mark Slee24b49d32007-03-21 01:24:00 +0000325\end{itemize}
326
327\subsection{Implementation}
328
329The transport interface is designed for simple implementation in any
330programming language. New transport mechanisms can be easily defined as needed
331by application developers.
332
333\subsubsection{TSocket}
334
335The \texttt{TSocket} class is implemented across all target languages. It
336provides a common, simple interface to a TCP/IP stream socket.
337
338\subsubsection{TFileTransport}
339
340The \texttt{TFileTransport} is an abstraction of an on-disk file to a data
Mark Sleeafaf2762007-04-03 19:47:04 +0000341stream. It can be used to write out a set of incoming Thrift requests to a file
342on disk. The on-disk data can then be replayed from the log, either for
343post-processing or for reproduction and/or simulation of past events.
Mark Slee24b49d32007-03-21 01:24:00 +0000344
345\subsubsection{Utilities}
346
347The Transport interface is designed to support easy extension using common
Roger Meier3f067a82011-03-04 13:35:05 +0000348OOP techniques, such as composition. Some simple utilities include the
Mark Sleeafaf2762007-04-03 19:47:04 +0000349\texttt{TBufferedTransport}, which buffers the writes and reads on an
350underlying transport, the \texttt{TFramedTransport}, which transmits data with frame
351size headers for chunking optimization or nonblocking operation, and the
352\texttt{TMemoryBuffer}, which allows reading and writing directly from the heap
353or stack memory owned by the process.
Mark Slee24b49d32007-03-21 01:24:00 +0000354
355\section{Protocol}
356
357A second major abstraction in Thrift is the separation of data structure from
358transport representation. Thrift enforces a certain messaging structure when
359transporting data, but it is agnostic to the protocol encoding in use. That is,
Mark Sleeafaf2762007-04-03 19:47:04 +0000360it does not matter whether data is encoded as XML, human-readable ASCII, or a
361dense binary format as long as the data supports a fixed set of operations
362that allow it to be deterministically read and written by generated code.
Mark Slee24b49d32007-03-21 01:24:00 +0000363
364\subsection{Interface}
365
366The Thrift Protocol interface is very straightforward. It fundamentally
367supports two things: 1) bidirectional sequenced messaging, and
3682) encoding of base types, containers, and structs.
369
370\begin{verbatim}
371writeMessageBegin(name, type, seq)
372writeMessageEnd()
373writeStructBegin(name)
374writeStructEnd()
375writeFieldBegin(name, type, id)
376writeFieldEnd()
377writeFieldStop()
378writeMapBegin(ktype, vtype, size)
379writeMapEnd()
380writeListBegin(etype, size)
381writeListEnd()
382writeSetBegin(etype, size)
383writeSetEnd()
384writeBool(bool)
385writeByte(byte)
386writeI16(i16)
387writeI32(i32)
388writeI64(i64)
389writeDouble(double)
390writeString(string)
391
392name, type, seq = readMessageBegin()
393 readMessageEnd()
394name = readStructBegin()
395 readStructEnd()
396name, type, id = readFieldBegin()
397 readFieldEnd()
398k, v, size = readMapBegin()
399 readMapEnd()
400etype, size = readListBegin()
401 readListEnd()
402etype, size = readSetBegin()
403 readSetEnd()
404bool = readBool()
405byte = readByte()
406i16 = readI16()
407i32 = readI32()
408i64 = readI64()
409double = readDouble()
410string = readString()
411\end{verbatim}
412
Mark Sleeafaf2762007-04-03 19:47:04 +0000413Note that every \texttt{write} function has exactly one \texttt{read} counterpart, with
414the exception of \texttt{writeFieldStop()}. This is a special method
Mark Slee24b49d32007-03-21 01:24:00 +0000415that signals the end of a struct. The procedure for reading a struct is to
Mark Sleeafaf2762007-04-03 19:47:04 +0000416\texttt{readFieldBegin()} until the stop field is encountered, and then to
Mark Slee24b49d32007-03-21 01:24:00 +0000417\texttt{readStructEnd()}. The
Mark Sleeafaf2762007-04-03 19:47:04 +0000418generated code relies upon this call sequence to ensure that everything written by
Mark Slee24b49d32007-03-21 01:24:00 +0000419a protocol encoder can be read by a matching protocol decoder. Further note
420that this set of functions is by design more robust than necessary.
421For example, \texttt{writeStructEnd()} is not strictly necessary, as the end of
422a struct may be implied by the stop field. This method is a convenience for
Mark Sleeafaf2762007-04-03 19:47:04 +0000423verbose protocols in which it is cleaner to separate these calls (e.g. a closing
Mark Slee24b49d32007-03-21 01:24:00 +0000424\texttt{</struct>} tag in XML).
425
426\subsection{Structure}
427
428Thrift structures are designed to support encoding into a streaming
Mark Sleeafaf2762007-04-03 19:47:04 +0000429protocol. The implementation should never need to frame or compute the
Mark Slee24b49d32007-03-21 01:24:00 +0000430entire data length of a structure prior to encoding it. This is critical to
431performance in many scenarios. Consider a long list of relatively large
Mark Sleeafaf2762007-04-03 19:47:04 +0000432strings. If the protocol interface required reading or writing a list to be an
433atomic operation, then the implementation would need to perform a linear pass over the
Mark Slee24b49d32007-03-21 01:24:00 +0000434entire list before encoding any data. However, if the list can be written
435as iteration is performed, the corresponding read may begin in parallel,
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000436theoretically offering an end-to-end speedup of $(kN - C)$, where $N$ is the size
Mark Slee24b49d32007-03-21 01:24:00 +0000437of the list, $k$ the cost factor associated with serializing a single
438element, and $C$ is fixed offset for the delay between data being written
439and becoming available to read.
440
441Similarly, structs do not encode their data lengths a priori. Instead, they are
442encoded as a sequence of fields, with each field having a type specifier and a
Mark Sleeafaf2762007-04-03 19:47:04 +0000443unique field identifier. Note that the inclusion of type specifiers allows
Mark Slee24b49d32007-03-21 01:24:00 +0000444the protocol to be safely parsed and decoded without any generated code
445or access to the original IDL file. Structs are terminated by a field header
446with a special \texttt{STOP} type. Because all the basic types can be read
Mark Sleeafaf2762007-04-03 19:47:04 +0000447deterministically, all structs (even those containing other structs) can be
Mark Slee24b49d32007-03-21 01:24:00 +0000448read deterministically. The Thrift protocol is self-delimiting without any
449framing and regardless of the encoding format.
450
451In situations where streaming is unnecessary or framing is advantageous, it
452can be very simply added into the transport layer, using the
453\texttt{TFramedTransport} abstraction.
454
455\subsection{Implementation}
456
457Facebook has implemented and deployed a space-efficient binary protocol which
458is used by most backend services. Essentially, it writes all data
459in a flat binary format. Integer types are converted to network byte order,
460strings are prepended with their byte length, and all message and field headers
461are written using the primitive integer serialization constructs. String names
462for fields are omitted - when using generated code, field identifiers are
463sufficient.
464
465We decided against some extreme storage optimizations (i.e. packing
466small integers into ASCII or using a 7-bit continuation format) for the sake
467of simplicity and clarity in the code. These alterations can easily be made
Mark Sleeafaf2762007-04-03 19:47:04 +0000468if and when we encounter a performance-critical use case that demands them.
Mark Slee24b49d32007-03-21 01:24:00 +0000469
470\section{Versioning}
471
472Thrift is robust in the face of versioning and data definition changes. This
Mark Sleeafaf2762007-04-03 19:47:04 +0000473is critical to enable staged rollouts of changes to deployed services. The
474system must be able to support reading of old data from log files, as well as
475requests from out-of-date clients to new servers, and vice versa.
Mark Slee24b49d32007-03-21 01:24:00 +0000476
477\subsection{Field Identifiers}
478
479Versioning in Thrift is implemented via field identifiers. The field header
480for every member of a struct in Thrift is encoded with a unique field
481identifier. The combination of this field identifier and its type specifier
482is used to uniquely identify the field. The Thrift definition language
483supports automatic assignment of field identifiers, but it is good
484programming practice to always explicitly specify field identifiers.
485Identifiers are specified as follows:
486
487\begin{verbatim}
488struct Example {
489 1:i32 number=10,
490 2:i64 bigNumber,
491 3:double decimals,
492 4:string name="thrifty"
493}\end{verbatim}
494
Mark Sleeafaf2762007-04-03 19:47:04 +0000495To avoid conflicts between manually and automatically assigned identifiers,
496fields with identifiers omitted are assigned identifiers
Mark Slee24b49d32007-03-21 01:24:00 +0000497decrementing from -1, and the language only supports the manual assignment of
498positive identifiers.
499
500When data is being deserialized, the generated code can use these identifiers
501to properly identify the field and determine whether it aligns with a field in
502its definition file. If a field identifier is not recognized, the generated
503code can use the type specifier to skip the unknown field without any error.
Mark Sleeafaf2762007-04-03 19:47:04 +0000504Again, this is possible due to the fact that all datatypes are self
Mark Slee24b49d32007-03-21 01:24:00 +0000505delimiting.
506
507Field identifiers can (and should) also be specified in function argument
508lists. In fact, argument lists are not only represented as structs on the
509backend, but actually share the same code in the compiler frontend. This
510allows for version-safe modification of method parameters
511
512\begin{verbatim}
513service StringCache {
514 void set(1:i32 key, 2:string value),
515 string get(1:i32 key) throws (1:KeyNotFound knf),
David Reiss0c90f6f2008-02-06 22:18:40 +0000516 void delete(1:i32 key)
Mark Slee24b49d32007-03-21 01:24:00 +0000517}
518\end{verbatim}
519
520The syntax for specifying field identifiers was chosen to echo their structure.
521Structs can be thought of as a dictionary where the identifiers are keys, and
Mark Sleeafaf2762007-04-03 19:47:04 +0000522the values are strongly-typed named fields.
Mark Slee24b49d32007-03-21 01:24:00 +0000523
524Field identifiers internally use the \texttt{i16} Thrift type. Note, however,
525that the \texttt{TProtocol} abstraction may encode identifiers in any format.
526
527\subsection{Isset}
528
529When an unexpected field is encountered, it can be safely ignored and
530discarded. When an expected field is not found, there must be some way to
531signal to the developer that it was not present. This is implemented via an
Mark Sleeafaf2762007-04-03 19:47:04 +0000532inner \texttt{isset} structure inside the defined objects. (Isset functionality
533is implicit with a \texttt{null} value in PHP, \texttt{None} in Python
Mark Slee24b49d32007-03-21 01:24:00 +0000534and \texttt{nil} in Ruby.) Essentially,
535the inner \texttt{isset} object of each Thrift struct contains a boolean value
536for each field which denotes whether or not that field is present in the
537struct. When a reader receives a struct, it should check for a field being set
538before operating directly on it.
539
540\begin{verbatim}
541class Example {
542 public:
543 Example() :
544 number(10),
545 bigNumber(0),
546 decimals(0),
David Reiss0c90f6f2008-02-06 22:18:40 +0000547 name("thrifty") {}
Mark Slee24b49d32007-03-21 01:24:00 +0000548
549 int32_t number;
550 int64_t bigNumber;
551 double decimals;
552 std::string name;
553
554 struct __isset {
555 __isset() :
556 number(false),
557 bigNumber(false),
558 decimals(false),
559 name(false) {}
560 bool number;
561 bool bigNumber;
562 bool decimals;
563 bool name;
564 } __isset;
565...
566}
David Reiss0c90f6f2008-02-06 22:18:40 +0000567\end{verbatim}
Mark Slee24b49d32007-03-21 01:24:00 +0000568
569\subsection{Case Analysis}
570
571There are four cases in which version mismatches may occur.
572
573\begin{enumerate}
574\item \textit{Added field, old client, new server.} In this case, the old
575client does not send the new field. The new server recognizes that the field
Mark Sleeafaf2762007-04-03 19:47:04 +0000576is not set, and implements default behavior for out-of-date requests.
Mark Slee24b49d32007-03-21 01:24:00 +0000577\item \textit{Removed field, old client, new server.} In this case, the old
578client sends the removed field. The new server simply ignores it.
579\item \textit{Added field, new client, old server.} The new client sends a
580field that the old server does not recognize. The old server simply ignores
581it and processes as normal.
582\item \textit{Removed field, new client, old server.} This is the most
583dangerous case, as the old server is unlikely to have suitable default
584behavior implemented for the missing field. It is recommended that in this
585situation the new server be rolled out prior to the new clients.
586\end{enumerate}
587
588\subsection{Protocol/Transport Versioning}
589The \texttt{TProtocol} abstractions are also designed to give protocol
590implementations the freedom to version themselves in whatever manner they
591see fit. Specifically, any protocol implementation is free to send whatever
592it likes in the \texttt{writeMessageBegin()} call. It is entirely up to the
593implementor how to handle versioning at the protocol level. The key point is
594that protocol encoding changes are safely isolated from interface definition
595version changes.
596
597Note that the exact same is true of the \texttt{TTransport} interface. For
598example, if we wished to add some new checksumming or error detection to the
599\texttt{TFileTransport}, we could simply add a version header into the
600data it writes to the file in such a way that it would still accept old
Mark Sleeafaf2762007-04-03 19:47:04 +0000601log files without the given header.
Mark Slee24b49d32007-03-21 01:24:00 +0000602
603\section{RPC Implementation}
604
605\subsection{TProcessor}
606
607The last core interface in the Thrift design is the \texttt{TProcessor},
608perhaps the most simple of the constructs. The interface is as follows:
609
610\begin{verbatim}
611interface TProcessor {
612 bool process(TProtocol in, TProtocol out)
613 throws TException
614}
615\end{verbatim}
616
617The key design idea here is that the complex systems we build can fundamentally
618be broken down into agents or services that operate on inputs and outputs. In
619most cases, there is actually just one input and output (an RPC client) that
620needs handling.
621
622\subsection{Generated Code}
623
624When a service is defined, we generate a
625\texttt{TProcessor} instance capable of handling RPC requests to that service,
626using a few helpers. The fundamental structure (illustrated in pseudo-C++) is
627as follows:
628
629\begin{verbatim}
630Service.thrift
631 => Service.cpp
632 interface ServiceIf
633 class ServiceClient : virtual ServiceIf
634 TProtocol in
635 TProtocol out
636 class ServiceProcessor : TProcessor
637 ServiceIf handler
638
639ServiceHandler.cpp
640 class ServiceHandler : virtual ServiceIf
641
642TServer.cpp
643 TServer(TProcessor processor,
644 TServerTransport transport,
645 TTransportFactory tfactory,
646 TProtocolFactory pfactory)
647 serve()
648\end{verbatim}
649
Mark Sleec11045a2007-04-01 23:14:38 +0000650From the Thrift definition file, we generate the virtual service interface.
Mark Slee24b49d32007-03-21 01:24:00 +0000651A client class is generated, which implements the interface and
652uses two \texttt{TProtocol} instances to perform the I/O operations. The
653generated processor implements the \texttt{TProcessor} interface. The generated
654code has all the logic to handle RPC invocations via the \texttt{process()}
Mark Sleeafaf2762007-04-03 19:47:04 +0000655call, and takes as a parameter an instance of the service interface, as
Mark Slee24b49d32007-03-21 01:24:00 +0000656implemented by the application developer.
657
Mark Sleeafaf2762007-04-03 19:47:04 +0000658The user provides an implementation of the application interface in separate,
659non-generated source code.
Mark Slee24b49d32007-03-21 01:24:00 +0000660
661\subsection{TServer}
662
663Finally, the Thrift core libraries provide a \texttt{TServer} abstraction.
664The \texttt{TServer} object generally works as follows.
665
666\begin{itemize}
667\item Use the \texttt{TServerTransport} to get a \texttt{TTransport}
668\item Use the \texttt{TTransportFactory} to optionally convert the primitive
669transport into a suitable application transport (typically the
670\texttt{TBufferedTransportFactory} is used here)
671\item Use the \texttt{TProtocolFactory} to create an input and output protocol
672for the \texttt{TTransport}
673\item Invoke the \texttt{process()} method of the \texttt{TProcessor} object
674\end{itemize}
675
676The layers are appropriately separated such that the server code needs to know
677nothing about any of the transports, encodings, or applications in play. The
678server encapsulates the logic around connection handling, threading, etc.
679while the processor deals with RPC. The only code written by the application
Mark Sleec11045a2007-04-01 23:14:38 +0000680developer lives in the definitional Thrift file and the interface
Mark Slee24b49d32007-03-21 01:24:00 +0000681implementation.
682
683Facebook has deployed multiple \texttt{TServer} implementations, including
684the single-threaded \texttt{TSimpleServer}, thread-per-connection
685\texttt{TThreadedServer}, and thread-pooling \texttt{TThreadPoolServer}.
686
687The \texttt{TProcessor} interface is very general by design. There is no
688requirement that a \texttt{TServer} take a generated \texttt{TProcessor}
689object. Thrift allows the application developer to easily write any type of
690server that operates on \texttt{TProtocol} objects (for instance, a server
691could simply stream a certain type of object without any actual RPC method
692invocation).
693
694\section{Implementation Details}
695\subsection{Target Languages}
696Thrift currently supports five target languages: C++, Java, Python, Ruby, and
697PHP. At Facebook, we have deployed servers predominantly in C++, Java, and
698Python. Thrift services implemented in PHP have also been embedded into the
699Apache web server, providing transparent backend access to many of our
700frontend constructs using a \texttt{THttpClient} implementation of the
701\texttt{TTransport} interface.
702
703Though Thrift was explicitly designed to be much more efficient and robust
704than typical web technologies, as we were designing our XML-based REST web
705services API we noticed that Thrift could be easily used to define our
706service interface. Though we do not currently employ SOAP envelopes (in the
Mark Sleeafaf2762007-04-03 19:47:04 +0000707authors' opinions there is already far too much repetitive enterprise Java
Mark Slee24b49d32007-03-21 01:24:00 +0000708software to do that sort of thing), we were able to quickly extend Thrift to
709generate XML Schema Definition files for our service, as well as a framework
710for versioning different implementations of our web service. Though public
711web services are admittedly tangential to Thrift's core use case and design,
712Thrift facilitated rapid iteration and affords us the ability to quickly
713migrate our entire XML-based web service onto a higher performance system
Mark Sleeafaf2762007-04-03 19:47:04 +0000714should the need arise.
Mark Slee24b49d32007-03-21 01:24:00 +0000715
716\subsection{Generated Structs}
717We made a conscious decision to make our generated structs as transparent as
718possible. All fields are publicly accessible; there are no \texttt{set()} and
719\texttt{get()} methods. Similarly, use of the \texttt{isset} object is not
720enforced. We do not include any \texttt{FieldNotSetException} construct.
721Developers have the option to use these fields to write more robust code, but
722the system is robust to the developer ignoring the \texttt{isset} construct
723entirely and will provide suitable default behavior in all cases.
724
Mark Sleeafaf2762007-04-03 19:47:04 +0000725This choice was motivated by the desire to ease application development. Our stated
Mark Slee24b49d32007-03-21 01:24:00 +0000726goal is not to make developers learn a rich new library in their language of
727choice, but rather to generate code that allow them to work with the constructs
728that are most familiar in each language.
729
730We also made the \texttt{read()} and \texttt{write()} methods of the generated
Mark Sleeafaf2762007-04-03 19:47:04 +0000731objects public so that the objects can be used outside of the context
Mark Slee24b49d32007-03-21 01:24:00 +0000732of RPC clients and servers. Thrift is a useful tool simply for generating
733objects that are easily serializable across programming languages.
734
735\subsection{RPC Method Identification}
736Method calls in RPC are implemented by sending the method name as a string. One
737issue with this approach is that longer method names require more bandwidth.
738We experimented with using fixed-size hashes to identify methods, but in the
739end concluded that the savings were not worth the headaches incurred. Reliably
740dealing with conflicts across versions of an interface definition file is
741impossible without a meta-storage system (i.e. to generate non-conflicting
742hashes for the current version of a file, we would have to know about all
743conflicts that ever existed in any previous version of the file).
744
745We wanted to avoid too many unnecessary string comparisons upon
746method invocation. To deal with this, we generate maps from strings to function
747pointers, so that invocation is effectively accomplished via a constant-time
748hash lookup in the common case. This requires the use of a couple interesting
749code constructs. Because Java does not have function pointers, process
750functions are all private member classes implementing a common interface.
751
752\begin{verbatim}
753private class ping implements ProcessFunction {
754 public void process(int seqid,
755 TProtocol iprot,
756 TProtocol oprot)
757 throws TException
758 { ...}
759}
760
761HashMap<String,ProcessFunction> processMap_ =
762 new HashMap<String,ProcessFunction>();
763\end{verbatim}
764
765In C++, we use a relatively esoteric language construct: member function
766pointers.
767
768\begin{verbatim}
769std::map<std::string,
770 void (ExampleServiceProcessor::*)(int32_t,
771 facebook::thrift::protocol::TProtocol*,
772 facebook::thrift::protocol::TProtocol*)>
773 processMap_;
774\end{verbatim}
775
776Using these techniques, the cost of string processing is minimized, and we
777reap the benefit of being able to easily debug corrupt or misunderstood data by
Mark Sleeafaf2762007-04-03 19:47:04 +0000778inspecting it for known string method names.
Mark Slee24b49d32007-03-21 01:24:00 +0000779
780\subsection{Servers and Multithreading}
Mark Sleeafaf2762007-04-03 19:47:04 +0000781Thrift services require basic multithreading to handle simultaneous
Mark Sleec11045a2007-04-01 23:14:38 +0000782requests from multiple clients. For the Python and Java implementations of
Mark Sleeafaf2762007-04-03 19:47:04 +0000783Thrift server logic, the standard threading libraries distributed with the
784languages provide adequate support. For the C++ implementation, no standard multithread runtime
David Reiss0c90f6f2008-02-06 22:18:40 +0000785library exists. Specifically, robust, lightweight, and portable
Mark Sleeafaf2762007-04-03 19:47:04 +0000786thread manager and timer class implementations do not exist. We investigated
David Reiss0c90f6f2008-02-06 22:18:40 +0000787existing implementations, namely \texttt{boost::thread},
Mark Sleec11045a2007-04-01 23:14:38 +0000788\texttt{boost::threadpool}, \texttt{ACE\_Thread\_Manager} and
David Reiss0c90f6f2008-02-06 22:18:40 +0000789\texttt{ACE\_Timer}.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000790
Mark Sleec11045a2007-04-01 23:14:38 +0000791While \texttt{boost::threads}\cite{boost.threads} provides clean,
792lightweight and robust implementations of multi-thread primitives (mutexes,
793conditions, threads) it does not provide a thread manager or timer
David Reiss0c90f6f2008-02-06 22:18:40 +0000794implementation.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000795
Mark Sleec11045a2007-04-01 23:14:38 +0000796\texttt{boost::threadpool}\cite{boost.threadpool} also looked promising but
797was not far enough along for our purposes. We wanted to limit the dependency on
Mark Sleeafaf2762007-04-03 19:47:04 +0000798third-party libraries as much as possible. Because\\
Mark Sleec11045a2007-04-01 23:14:38 +0000799\texttt{boost::threadpool} is
800not a pure template library and requires runtime libraries and because it is
Mark Sleeafaf2762007-04-03 19:47:04 +0000801not yet part of the official Boost distribution we felt it was not ready for
Mark Sleec11045a2007-04-01 23:14:38 +0000802use in Thrift. As \texttt{boost::threadpool} evolves and especially if it is
Mark Sleeafaf2762007-04-03 19:47:04 +0000803added to the Boost distribution we may reconsider our decision to not use it.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000804
805ACE has both a thread manager and timer class in addition to multi-thread
Mark Sleeafaf2762007-04-03 19:47:04 +0000806primitives. The biggest problem with ACE is that it is ACE. Unlike Boost, ACE
Mark Sleec11045a2007-04-01 23:14:38 +0000807API quality is poor. Everything in ACE has large numbers of dependencies on
David Reiss0c90f6f2008-02-06 22:18:40 +0000808everything else in ACE - thus forcing developers to throw out standard
Mark Sleeafaf2762007-04-03 19:47:04 +0000809classes, such as STL collections, in favor of ACE's homebrewed implementations. In
810addition, unlike Boost, ACE implementations demonstrate little understanding
Mark Sleec11045a2007-04-01 23:14:38 +0000811of the power and pitfalls of C++ programming and take no advantage of modern
812templating techniques to ensure compile time safety and reasonable compiler
Mark Sleeafaf2762007-04-03 19:47:04 +0000813error messages. For all these reasons, ACE was rejected. Instead, we chose
814to implement our own library, described in the following sections.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000815
816\subsection{Thread Primitives}
817
Mark Sleec11045a2007-04-01 23:14:38 +0000818The Thrift thread libraries are implemented in the namespace\\
819\texttt{facebook::thrift::concurrency} and have three components:
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000820\begin{itemize}
Mark Sleec11045a2007-04-01 23:14:38 +0000821\item primitives
822\item thread pool manager
823\item timer manager
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000824\end{itemize}
825
Mark Sleec11045a2007-04-01 23:14:38 +0000826As mentioned above, we were hesitant to introduce any additional dependencies
David Reiss0c90f6f2008-02-06 22:18:40 +0000827on Thrift. We decided to use \texttt{boost::shared\_ptr} because it is so
Mark Sleeafaf2762007-04-03 19:47:04 +0000828useful for multithreaded application, it requires no link-time or
829runtime libraries (i.e. it is a pure template library) and it is due
830to become part of the C++0x standard.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000831
Mark Sleec11045a2007-04-01 23:14:38 +0000832We implement standard \texttt{Mutex} and \texttt{Condition} classes, and a
David Reiss0c90f6f2008-02-06 22:18:40 +0000833 \texttt{Monitor} class. The latter is simply a combination of a mutex and
Mark Sleeafaf2762007-04-03 19:47:04 +0000834condition variable and is analogous to the \texttt{Monitor} implementation provided for
David Reiss0c90f6f2008-02-06 22:18:40 +0000835the Java \texttt{Object} class. This is also sometimes referred to as a barrier. We
Mark Sleec11045a2007-04-01 23:14:38 +0000836provide a \texttt{Synchronized} guard class to allow Java-like synchronized blocks.
David Reiss0c90f6f2008-02-06 22:18:40 +0000837This is just a bit of syntactic sugar, but, like its Java counterpart, clearly
Mark Sleeafaf2762007-04-03 19:47:04 +0000838delimits critical sections of code. Unlike its Java counterpart, we still
Mark Sleec11045a2007-04-01 23:14:38 +0000839have the ability to programmatically lock, unlock, block, and signal monitors.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000840
841\begin{verbatim}
Mark Sleec11045a2007-04-01 23:14:38 +0000842void run() {
843 {Synchronized s(manager->monitor);
844 if (manager->state == TimerManager::STARTING) {
845 manager->state = TimerManager::STARTED;
846 manager->monitor.notifyAll();
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000847 }
Mark Sleec11045a2007-04-01 23:14:38 +0000848 }
849}
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000850\end{verbatim}
851
Mark Sleec11045a2007-04-01 23:14:38 +0000852We again borrowed from Java the distinction between a thread and a runnable
853class. A \texttt{Thread} is the actual schedulable object. The
David Reiss0c90f6f2008-02-06 22:18:40 +0000854\texttt{Runnable} is the logic to execute within the thread.
855The \texttt{Thread} implementation deals with all the platform-specific thread
Mark Sleeafaf2762007-04-03 19:47:04 +0000856creation and destruction issues, while the \texttt{Runnable} implementation deals
Mark Sleec11045a2007-04-01 23:14:38 +0000857with the application-specific per-thread logic. The benefit of this approach
David Reiss0c90f6f2008-02-06 22:18:40 +0000858is that developers can easily subclass the Runnable class without pulling in
Aditya Agarwal8c5daa42007-04-05 01:17:52 +0000859platform-specific super-classes.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000860
861\subsection{Thread, Runnable, and shared\_ptr}
Mark Sleec11045a2007-04-01 23:14:38 +0000862We use \texttt{boost::shared\_ptr} throughout the \texttt{ThreadManager} and
863\texttt{TimerManager} implementations to guarantee cleanup of dead objects that can
864be accessed by multiple threads. For \texttt{Thread} class implementations,
865\texttt{boost::shared\_ptr} usage requires particular attention to make sure
866\texttt{Thread} objects are neither leaked nor dereferenced prematurely while
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000867creating and shutting down threads.
868
Mark Sleec11045a2007-04-01 23:14:38 +0000869Thread creation requires calling into a C library. (In our case the POSIX
Mark Slee09bfd612007-04-04 19:56:41 +0000870thread library, \texttt{libpthread}, but the same would be true for WIN32 threads).
Mark Sleeafaf2762007-04-03 19:47:04 +0000871Typically, 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 +0000872possible that our thread create call,
Mark Sleec11045a2007-04-01 23:14:38 +0000873\texttt{ThreadFactory::newThread()} could return to the caller
874well before that time. To ensure that the returned \texttt{Thread} object is not
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000875prematurely cleaned up if the caller gives up its reference prior to the
Roger Meier3f067a82011-03-04 13:35:05 +0000876\texttt{ThreadMain} call, the \texttt{Thread} object makes a weak reference to
Mark Sleec11045a2007-04-01 23:14:38 +0000877itself in its \texttt{start} method.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000878
Mark Sleec11045a2007-04-01 23:14:38 +0000879With the weak reference in hand the \texttt{ThreadMain} function can attempt to get
880a strong reference before entering the \texttt{Runnable::run} method of the
Mark Sleeafaf2762007-04-03 19:47:04 +0000881\texttt{Runnable} object bound to the \texttt{Thread}. If no strong references to the
David Reiss0c90f6f2008-02-06 22:18:40 +0000882thread 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 +0000883exits immediately.
884
Mark Sleec11045a2007-04-01 23:14:38 +0000885The need for the \texttt{Thread} to make a weak reference to itself has a
886significant impact on the API. Since references are managed through the
887\texttt{boost::shared\_ptr} templates, the \texttt{Thread} object must have a reference
888to itself wrapped by the same \texttt{boost::shared\_ptr} envelope that is returned
Mark Sleeafaf2762007-04-03 19:47:04 +0000889to the caller. This necessitated the use of the factory pattern.
890\texttt{ThreadFactory} creates the raw \texttt{Thread} object and a
891\texttt{boost::shared\_ptr} wrapper, and calls a private helper method of the class
892implementing the \texttt{Thread} interface (in this case, \texttt{PosixThread::weakRef})
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000893 to allow it to make add weak reference to itself through the
Mark Sleec11045a2007-04-01 23:14:38 +0000894 \texttt{boost::shared\_ptr} envelope.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000895
Mark Sleec11045a2007-04-01 23:14:38 +0000896\texttt{Thread} and \texttt{Runnable} objects reference each other. A \texttt{Runnable}
Mark Sleeafaf2762007-04-03 19:47:04 +0000897object may need to know about the thread in which it is executing, and a Thread, obviously,
Mark Sleec11045a2007-04-01 23:14:38 +0000898needs to know what \texttt{Runnable} object it is hosting. This interdependency is
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000899further complicated because the lifecycle of each object is independent of the
Mark Slee8142b9d2007-04-03 05:49:12 +0000900other. 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 +0000901once a thread has been created and started for it.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000902
Mark Sleec11045a2007-04-01 23:14:38 +0000903The \texttt{Thread} class takes a \texttt{boost::shared\_ptr} reference to the hosted
Mark Sleeafaf2762007-04-03 19:47:04 +0000904\texttt{Runnable} object in its constructor, while the \texttt{Runnable} class has an
Mark Sleec11045a2007-04-01 23:14:38 +0000905explicit \texttt{thread} method to allow explicit binding of the hosted thread.
Mark Sleeafaf2762007-04-03 19:47:04 +0000906\texttt{ThreadFactory::newThread} binds the objects to each other.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000907
908\subsection{ThreadManager}
909
David Reiss0c90f6f2008-02-06 22:18:40 +0000910\texttt{ThreadManager} creates a pool of worker threads and
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000911allows applications to schedule tasks for execution as free worker threads
David Reiss0c90f6f2008-02-06 22:18:40 +0000912become available. The \texttt{ThreadManager} does not implement dynamic
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000913thread pool resizing, but provides primitives so that applications can add
David Reiss0c90f6f2008-02-06 22:18:40 +0000914and remove threads based on load. This approach was chosen because
915implementing load metrics and thread pool size is very application
Mark Sleec11045a2007-04-01 23:14:38 +0000916specific. For example some applications may want to adjust pool size based
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000917on running-average of work arrival rates that are measured via polled
Mark Sleec11045a2007-04-01 23:14:38 +0000918samples. Others may simply wish to react immediately to work-queue
919depth high and low water marks. Rather than trying to create a complex
David Reiss0c90f6f2008-02-06 22:18:40 +0000920API abstract enough to capture these different approaches, we
921simply leave it up to the particular application and provide the
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000922primitives to enact the desired policy and sample current status.
923
924\subsection{TimerManager}
925
Mark Sleeafaf2762007-04-03 19:47:04 +0000926\texttt{TimerManager} allows applications to schedule
David Reiss0c90f6f2008-02-06 22:18:40 +0000927 \texttt{Runnable} objects for execution at some point in the future. Its specific task
Mark Sleec11045a2007-04-01 23:14:38 +0000928is to allows applications to sample \texttt{ThreadManager} load at regular
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000929intervals and make changes to the thread pool size based on application policy.
Mark Sleec11045a2007-04-01 23:14:38 +0000930Of course, it can be used to generate any number of timer or alarm events.
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000931
Mark Sleec11045a2007-04-01 23:14:38 +0000932The default implementation of \texttt{TimerManager} uses a single thread to
David Reiss0c90f6f2008-02-06 22:18:40 +0000933execute expired \texttt{Runnable} objects. Thus, if a timer operation needs to
Marc Slemko10b3bdb2007-04-01 09:14:05 +0000934do a large amount of work and especially if it needs to do blocking I/O,
935that should be done in a separate thread.
Mark Slee24b49d32007-03-21 01:24:00 +0000936
937\subsection{Nonblocking Operation}
938Though the Thrift transport interfaces map more directly to a blocking I/O
939model, we have implemented a high performance \texttt{TNonBlockingServer}
Mark Sleeafaf2762007-04-03 19:47:04 +0000940in C++ based on \texttt{libevent} and the \texttt{TFramedTransport}. We
Mark Slee24b49d32007-03-21 01:24:00 +0000941implemented this by moving all I/O into one tight event loop using a
942state machine. Essentially, the event loop reads framed requests into
943\texttt{TMemoryBuffer} objects. Once entire requests are ready, they are
944dispatched to the \texttt{TProcessor} object which can read directly from
945the data in memory.
946
947\subsection{Compiler}
Mark Sleeafaf2762007-04-03 19:47:04 +0000948The Thrift compiler is implemented in C++ using standard \texttt{lex}/\texttt{yacc}
949lexing and parsing. Though it could have been implemented with fewer
950lines of code in another language (i.e. Python Lex-Yacc (PLY) or \texttt{ocamlyacc}), using C++
Mark Slee24b49d32007-03-21 01:24:00 +0000951forces explicit definition of the language constructs. Strongly typing the
952parse tree elements (debatably) makes the code more approachable for new
953developers.
954
955Code generation is done using two passes. The first pass looks only for
956include files and type definitions. Type definitions are not checked during
957this phase, since they may depend upon include files. All included files
958are sequentially scanned in a first pass. Once the include tree has been
Mark Sleeafaf2762007-04-03 19:47:04 +0000959resolved, a second pass over all files is taken that inserts type definitions
Mark Slee24b49d32007-03-21 01:24:00 +0000960into the parse tree and raises an error on any undefined types. The program is
961then generated against the parse tree.
962
963Due to inherent complexities and potential for circular dependencies,
964we explicitly disallow forward declaration. Two Thrift structs cannot
965each contain an instance of the other. (Since we do not allow \texttt{null}
966struct instances in the generated C++ code, this would actually be impossible.)
967
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000968\subsection{TFileTransport}
David Reiss0c90f6f2008-02-06 22:18:40 +0000969The \texttt{TFileTransport} logs Thrift requests/structs by
970framing incoming data with its length and writing it out to disk.
971Using a framed on-disk format allows for better error checking and
Mark Sleeafaf2762007-04-03 19:47:04 +0000972helps with the processing of a finite number of discrete events. The\\
David Reiss0c90f6f2008-02-06 22:18:40 +0000973\texttt{TFileWriterTransport} uses a system of swapping in-memory buffers
974to ensure good performance while logging large amounts of data.
Mark Sleeafaf2762007-04-03 19:47:04 +0000975A Thrift log file is split up into chunks of a specified size; logged messages
David Reiss0c90f6f2008-02-06 22:18:40 +0000976are not allowed to cross chunk boundaries. A message that would cross a chunk
977boundary will cause padding to be added until the end of the chunk and the
Mark Sleeafaf2762007-04-03 19:47:04 +0000978first byte of the message are aligned to the beginning of the next chunk.
David Reiss0c90f6f2008-02-06 22:18:40 +0000979Partitioning the file into chunks makes it possible to read and interpret data
980from a particular point in the file.
Aditya Agarwalaf524ee2007-03-31 08:28:06 +0000981
Mark Sleec11045a2007-04-01 23:14:38 +0000982\section{Facebook Thrift Services}
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000983Thrift has been employed in a large number of applications at Facebook, including
Mark Sleeafaf2762007-04-03 19:47:04 +0000984search, logging, mobile, ads and the developer platform. Two specific usages are discussed below.
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000985
986\subsection{Search}
Mark Sleeafaf2762007-04-03 19:47:04 +0000987Thrift is used as the underlying protocol and transport layer for the Facebook Search service.
988The multi-language code generation is well suited for search because it allows for application
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000989development in an efficient server side language (C++) and allows the Facebook PHP-based web application
990to make calls to the search service using Thrift PHP libraries. There is also a large
David Reiss0c90f6f2008-02-06 22:18:40 +0000991variety of search stats, deployment and testing functionality that is built on top
Mark Sleeafaf2762007-04-03 19:47:04 +0000992of generated Python code. Additionally, the Thrift log file format is
David Reiss0c90f6f2008-02-06 22:18:40 +0000993used as a redo log for providing real-time search index updates. Thrift has allowed the
994search team to leverage each language for its strengths and to develop code at a rapid pace.
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +0000995
996\subsection{Logging}
997The Thrift \texttt{TFileTransport} functionality is used for structured logging. Each
998service function definition along with its parameters can be considered to be
David Reiss0c90f6f2008-02-06 22:18:40 +0000999a structured log entry identified by the function name. This log can then be used for
Mark Sleeafaf2762007-04-03 19:47:04 +00001000a variety of purposes, including inline and offline processing, stats aggregation and as a redo log.
Aditya Agarwaladf3e7f2007-03-31 16:56:14 +00001001
Mark Slee24b49d32007-03-21 01:24:00 +00001002\section{Conclusions}
1003Thrift has enabled Facebook to build scalable backend
1004services efficiently by enabling engineers to divide and conquer. Application
Mark Sleeafaf2762007-04-03 19:47:04 +00001005developers can focus on application code without worrying about the
Mark Slee24b49d32007-03-21 01:24:00 +00001006sockets layer. We avoid duplicated work by writing buffering and I/O logic
1007in one place, rather than interspersing it in each application.
1008
1009Thrift has been employed in a wide variety of applications at Facebook,
Mark Sleeafaf2762007-04-03 19:47:04 +00001010including search, logging, mobile, ads, and the developer platform. We have
Mark Slee24b49d32007-03-21 01:24:00 +00001011found that the marginal performance cost incurred by an extra layer of
Mark Sleeafaf2762007-04-03 19:47:04 +00001012software abstraction is far eclipsed by the gains in developer efficiency and
Mark Slee24b49d32007-03-21 01:24:00 +00001013systems reliability.
1014
1015\appendix
1016
1017\section{Similar Systems}
1018The following are software systems similar to Thrift. Each is (very!) briefly
1019described:
1020
1021\begin{itemize}
1022\item \textit{SOAP.} XML-based. Designed for web services via HTTP, excessive
1023XML parsing overhead.
1024\item \textit{CORBA.} Relatively comprehensive, debatably overdesigned and
1025heavyweight. Comparably cumbersome software installation.
Roger Meier3f067a82011-03-04 13:35:05 +00001026\item \textit{COM.} Embraced mainly in Windows client software. Not an entirely
Mark Slee24b49d32007-03-21 01:24:00 +00001027open solution.
1028\item \textit{Pillar.} Lightweight and high-performance, but missing versioning
1029and abstraction.
1030\item \textit{Protocol Buffers.} Closed-source, owned by Google. Described in
1031Sawzall paper.
1032\end{itemize}
1033
1034\acks
1035
1036Many thanks for feedback on Thrift (and extreme trial by fire) are due to
Aditya Agarwalaf524ee2007-03-31 08:28:06 +00001037Martin Smith, Karl Voskuil and Yishan Wong.
Mark Slee24b49d32007-03-21 01:24:00 +00001038
1039Thrift is a successor to Pillar, a similar system developed
1040by Adam D'Angelo, first while at Caltech and continued later at Facebook.
1041Thrift simply would not have happened without Adam's insights.
1042
Marc Slemko10b3bdb2007-04-01 09:14:05 +00001043\begin{thebibliography}{}
Mark Slee24b49d32007-03-21 01:24:00 +00001044
Marc Slemko10b3bdb2007-04-01 09:14:05 +00001045\bibitem{boost.threads}
1046Kempf, William,
1047``Boost.Threads'',
1048\url{http://www.boost.org/doc/html/threads.html}
Mark Slee24b49d32007-03-21 01:24:00 +00001049
Marc Slemko10b3bdb2007-04-01 09:14:05 +00001050\bibitem{boost.threadpool}
1051Henkel, Philipp,
1052``threadpool'',
1053\url{http://threadpool.sourceforge.net}
1054
1055\end{thebibliography}
Mark Slee24b49d32007-03-21 01:24:00 +00001056
1057\end{document}