r1407@opsdev009 (orig r74442): dreiss | 2007-12-14 15:46:47 -0800
[amiethrift.git] / doc / thrift.tex
blobfc1e6ba1ebb3205dcb788f6b7aaec6d9318d38ed
1 %-----------------------------------------------------------------------------
3 % Thrift whitepaper
5 % Name: thrift.tex
7 % Authors: Mark Slee (mcslee@facebook.com)
9 % Created: 05 March 2007
11 %-----------------------------------------------------------------------------
14 \documentclass[nocopyrightspace,blockstyle]{sigplanconf}
16 \usepackage{amssymb}
17 \usepackage{amsfonts}
18 \usepackage{amsmath}
19 \usepackage{url}
21 \begin{document}
23 % \conferenceinfo{WXYZ '05}{date, City.}
24 % \copyrightyear{2007}
25 % \copyrightdata{[to be supplied]}
27 % \titlebanner{banner above paper title} % These are ignored unless
28 % \preprintfooter{short description of paper} % 'preprint' option specified.
30 \title{Thrift: Scalable Cross-Language Services Implementation}
31 \subtitle{}
33 \authorinfo{Mark Slee, Aditya Agarwal and Marc Kwiatkowski}
34 {Facebook, 156 University Ave, Palo Alto, CA}
35 {\{mcslee,aditya,marc\}@facebook.com}
37 \maketitle
39 \begin{abstract}
40 Thrift is a software library and set of code-generation tools developed at
41 Facebook to expedite development and implementation of efficient and scalable
42 backend services. Its primary goal is to enable efficient and reliable
43 communication across programming languages by abstracting the portions of each
44 language that tend to require the most customization into a common library
45 that is implemented in each language. Specifically, Thrift allows developers to
46 define datatypes and service interfaces in a single language-neutral file
47 and generate all the necessary code to build RPC clients and servers.
49 This paper details the motivations and design choices we made in Thrift, as
50 well as some of the more interesting implementation details. It is not
51 intended to be taken as research, but rather it is an exposition on what we did
52 and why.
53 \end{abstract}
55 % \category{D.3.3}{Programming Languages}{Language constructs and features}
57 %\terms
58 %Languages, serialization, remote procedure call
60 %\keywords
61 %Data description language, interface definition language, remote procedure call
63 \section{Introduction}
64 As Facebook's traffic and network structure have scaled, the resource
65 demands of many operations on the site (i.e. search,
66 ad selection and delivery, event logging) have presented technical requirements
67 drastically outside the scope of the LAMP framework. In our implementation of
68 these services, various programming languages have been selected to
69 optimize for the right combination of performance, ease and speed of
70 development, availability of existing libraries, etc. By and large,
71 Facebook's engineering culture has tended towards choosing the best
72 tools and implementations available over standardizing on any one
73 programming language and begrudgingly accepting its inherent limitations.
75 Given this design choice, we were presented with the challenge of building
76 a transparent, high-performance bridge across many programming languages.
77 We found that most available solutions were either too limited, did not offer
78 sufficient datatype freedom, or suffered from subpar performance.
79 \footnote{See Appendix A for a discussion of alternative systems.}
81 The solution that we have implemented combines a language-neutral software
82 stack implemented across numerous programming languages and an associated code
83 generation engine that transforms a simple interface and data definition
84 language into client and server remote procedure call libraries.
85 Choosing static code generation over a dynamic system allows us to create
86 validated code that can be run without the need for
87 any advanced introspective run-time type checking. It is also designed to
88 be as simple as possible for the developer, who can typically define all
89 the necessary data structures and interfaces for a complex service in a single
90 short file.
92 Surprised that a robust open solution to these relatively common problems
93 did not yet exist, we committed early on to making the Thrift implementation
94 open source.
96 In evaluating the challenges of cross-language interaction in a networked
97 environment, some key components were identified:
99 \textit{Types.} A common type system must exist across programming languages
100 without requiring that the application developer use custom Thrift datatypes
101 or write their own serialization code. That is,
102 a C++ programmer should be able to transparently exchange a strongly typed
103 STL map for a dynamic Python dictionary. Neither
104 programmer should be forced to write any code below the application layer
105 to achieve this. Section 2 details the Thrift type system.
107 \textit{Transport.} Each language must have a common interface to
108 bidirectional raw data transport. The specifics of how a given
109 transport is implemented should not matter to the service developer.
110 The same application code should be able to run against TCP stream sockets,
111 raw data in memory, or files on disk. Section 3 details the Thrift Transport
112 layer.
114 \textit{Protocol.} Datatypes must have some way of using the Transport
115 layer to encode and decode themselves. Again, the application
116 developer need not be concerned by this layer. Whether the service uses
117 an XML or binary protocol is immaterial to the application code.
118 All that matters is that the data can be read and written in a consistent,
119 deterministic matter. Section 4 details the Thrift Protocol layer.
121 \textit{Versioning.} For robust services, the involved datatypes must
122 provide a mechanism for versioning themselves. Specifically,
123 it should be possible to add or remove fields in an object or alter the
124 argument list of a function without any interruption in service (or,
125 worse yet, nasty segmentation faults). Section 5 details Thrift's versioning
126 system.
128 \textit{Processors.} Finally, we generate code capable of processing data
129 streams to accomplish remote procedure calls. Section 6 details the generated
130 code and TProcessor paradigm.
132 Section 7 discusses implementation details, and Section 8 describes
133 our conclusions.
135 \section{Types}
137 The goal of the Thrift type system is to enable programmers to develop using
138 completely natively defined types, no matter what programming language they
139 use. By design, the Thrift type system does not introduce any special dynamic
140 types or wrapper objects. It also does not require that the developer write
141 any code for object serialization or transport. The Thrift IDL (Interface
142 Definition Language) file is
143 logically a way for developers to annotate their data structures with the
144 minimal amount of extra information necessary to tell a code generator
145 how to safely transport the objects across languages.
147 \subsection{Base Types}
149 The type system rests upon a few base types. In considering which types to
150 support, we aimed for clarity and simplicity over abundance, focusing
151 on the key types available in all programming languages, ommitting any
152 niche types available only in specific languages.
154 The 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}
165 Of particular note is the absence of unsigned integer types. Because these
166 types have no direct translation to native primitive types in many languages,
167 the advantages they afford are lost. Further, there is no way to prevent the
168 application developer in a language like Python from assigning a negative value
169 to an integer variable, leading to unpredictable behavior. From a design
170 standpoint, we observed that unsigned integers were very rarely, if ever, used
171 for arithmetic purposes, but in practice were much more often used as keys or
172 identifiers. In this case, the sign is irrelevant. Signed integers serve this
173 same purpose and can be safely cast to their unsigned counterparts (most
174 commonly in C++) when absolutely necessary.
176 \subsection{Structs}
178 A Thrift struct defines a common object to be used across languages. A struct
179 is essentially equivalent to a class in object oriented programming
180 languages. A struct has a set of strongly typed fields, each with a unique
181 name identifier. The basic syntax for defining a Thrift struct looks very
182 similar to a C struct definition. Fields may be annotated with an integer field
183 identifier (unique to the scope of that struct) and optional default values.
184 Field identifiers will be automatically assigned if omitted, though they are
185 strongly encouraged for versioning reasons discussed later.
187 \subsection{Containers}
189 Thrift containers are strongly typed containers that map to the most commonly
190 used containers in common programming languages. They are annotated using
191 the 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
194 an STL \texttt{vector}, Java \texttt{ArrayList}, or native array in scripting languages. May
195 contain duplicates.
196 \item \texttt{set<type>} An unordered set of unique elements. Translates into
197 an STL \texttt{set}, Java \texttt{HashSet}, \texttt{set} in Python, or native
198 dictionary in PHP/Ruby.
199 \item \texttt{map<type1,type2>} A map of strictly unique keys to values
200 Translates into an STL \texttt{map}, Java \texttt{HashMap}, PHP associative
201 array, or Python/Ruby dictionary.
202 \end{itemize}
204 While defaults are provided, the type mappings are not explicitly fixed. Custom
205 code generator directives have been added to substitute custom types in
206 destination languages (i.e.
207 \texttt{hash\_map} or Google's sparse hash map can be used in C++). The
208 only requirement is that the custom types support all the necessary iteration
209 primitives. Container elements may be of any valid Thrift type, including other
210 containers or structs.
212 \begin{verbatim}
213 struct Example {
214 1:i32 number=10,
215 2:i64 bigNumber,
216 3:double decimals,
217 4:string name="thrifty"
218 }\end{verbatim}
220 In the target language, each definition generates a type with two methods,
221 \texttt{read} and \texttt{write}, which perform serialization and transport
222 of the objects using a Thrift TProtocol object.
224 \subsection{Exceptions}
226 Exceptions are syntactically and functionally equivalent to structs except
227 that they are declared using the \texttt{exception} keyword instead of the
228 \texttt{struct} keyword.
230 The generated objects inherit from an exception base class as appropriate
231 in each target programming language, in order to seamlessly
232 integrate with native exception handling in any given
233 language. Again, the design emphasis is on making the code familiar to the
234 application developer.
236 \subsection{Services}
238 Services are defined using Thrift types. Definition of a service is
239 semantically equivalent to defining an interface (or a pure virtual abstract
240 class) in object oriented
241 programming. The Thrift compiler generates fully functional client and
242 server stubs that implement the interface. Services are defined as follows:
244 \begin{verbatim}
245 service <name> {
246 <returntype> <name>(<arguments>)
247 [throws (<exceptions>)]
249 }\end{verbatim}
251 An example:
253 \begin{verbatim}
254 service StringCache {
255 void set(1:i32 key, 2:string value),
256 string get(1:i32 key) throws (1:KeyNotFound knf),
257 void delete(1:i32 key)
259 \end{verbatim}
261 Note that \texttt{void} is a valid type for a function return, in addition to
262 all other defined Thrift types. Additionally, an \texttt{async} modifier
263 keyword may be added to a \texttt{void} function, which will generate code that does
264 not wait for a response from the server. Note that a pure \texttt{void}
265 function will return a response to the client which guarantees that the
266 operation has completed on the server side. With \texttt{async} method calls
267 the client will only be guaranteed that the request succeeded at the
268 transport layer. (In many transport scenarios this is inherently unreliable
269 due to the Byzantine Generals' Problem. Therefore, application developers
270 should take care only to use the async optimization in cases where dropped
271 method calls are acceptable or the transport is known to be reliable.)
273 Also of note is the fact that argument lists and exception lists for functions
274 are implemented as Thrift structs. All three constructs are identical in both
275 notation and behavior.
277 \section{Transport}
279 The transport layer is used by the generated code to facilitate data transfer.
281 \subsection{Interface}
283 A key design choice in the implementation of Thrift was to decouple the
284 transport layer from the code generation layer. Though Thrift is typically
285 used on top of the TCP/IP stack with streaming sockets as the base layer of
286 communication, there was no compelling reason to build that constraint into
287 the system. The performance tradeoff incurred by an abstracted I/O layer
288 (roughly one virtual method lookup / function call per operation) was
289 immaterial compared to the cost of actual I/O operations (typically invoking
290 system calls).
292 Fundamentally, generated Thrift code only needs to know how to read and
293 write data. The origin and destination of the data are irrelevant; it may be a
294 socket, a segment of shared memory, or a file on the local disk. The Thrift
295 transport interface supports the following methods:
297 \begin{itemize}
298 \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
304 \end{itemize}
306 There are a few additional methods not documented here which are used to aid
307 in batching reads and optionally signaling the completion of a read or
308 write operation from the generated code.
310 In addition to the above
311 \texttt{TTransport} interface, there is a\\
312 \texttt{TServerTransport} interface
313 used to accept or create primitive transport objects. Its interface is as
314 follows:
316 \begin{itemize}
317 \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
321 \end{itemize}
323 \subsection{Implementation}
325 The transport interface is designed for simple implementation in any
326 programming language. New transport mechanisms can be easily defined as needed
327 by application developers.
329 \subsubsection{TSocket}
331 The \texttt{TSocket} class is implemented across all target languages. It
332 provides a common, simple interface to a TCP/IP stream socket.
334 \subsubsection{TFileTransport}
336 The \texttt{TFileTransport} is an abstraction of an on-disk file to a data
337 stream. It can be used to write out a set of incoming Thrift requests to a file
338 on disk. The on-disk data can then be replayed from the log, either for
339 post-processing or for reproduction and/or simulation of past events.
341 \subsubsection{Utilities}
343 The Transport interface is designed to support easy extension using common
344 OOP techniques, such as composition. Some simple utilites include the
345 \texttt{TBufferedTransport}, which buffers the writes and reads on an
346 underlying transport, the \texttt{TFramedTransport}, which transmits data with frame
347 size headers for chunking optimization or nonblocking operation, and the
348 \texttt{TMemoryBuffer}, which allows reading and writing directly from the heap
349 or stack memory owned by the process.
351 \section{Protocol}
353 A second major abstraction in Thrift is the separation of data structure from
354 transport representation. Thrift enforces a certain messaging structure when
355 transporting data, but it is agnostic to the protocol encoding in use. That is,
356 it does not matter whether data is encoded as XML, human-readable ASCII, or a
357 dense binary format as long as the data supports a fixed set of operations
358 that allow it to be deterministically read and written by generated code.
360 \subsection{Interface}
362 The Thrift Protocol interface is very straightforward. It fundamentally
363 supports two things: 1) bidirectional sequenced messaging, and
364 2) encoding of base types, containers, and structs.
366 \begin{verbatim}
367 writeMessageBegin(name, type, seq)
368 writeMessageEnd()
369 writeStructBegin(name)
370 writeStructEnd()
371 writeFieldBegin(name, type, id)
372 writeFieldEnd()
373 writeFieldStop()
374 writeMapBegin(ktype, vtype, size)
375 writeMapEnd()
376 writeListBegin(etype, size)
377 writeListEnd()
378 writeSetBegin(etype, size)
379 writeSetEnd()
380 writeBool(bool)
381 writeByte(byte)
382 writeI16(i16)
383 writeI32(i32)
384 writeI64(i64)
385 writeDouble(double)
386 writeString(string)
388 name, type, seq = readMessageBegin()
389 readMessageEnd()
390 name = readStructBegin()
391 readStructEnd()
392 name, type, id = readFieldBegin()
393 readFieldEnd()
394 k, v, size = readMapBegin()
395 readMapEnd()
396 etype, size = readListBegin()
397 readListEnd()
398 etype, size = readSetBegin()
399 readSetEnd()
400 bool = readBool()
401 byte = readByte()
402 i16 = readI16()
403 i32 = readI32()
404 i64 = readI64()
405 double = readDouble()
406 string = readString()
407 \end{verbatim}
409 Note that every \texttt{write} function has exactly one \texttt{read} counterpart, with
410 the exception of \texttt{writeFieldStop()}. This is a special method
411 that signals the end of a struct. The procedure for reading a struct is to
412 \texttt{readFieldBegin()} until the stop field is encountered, and then to
413 \texttt{readStructEnd()}. The
414 generated code relies upon this call sequence to ensure that everything written by
415 a protocol encoder can be read by a matching protocol decoder. Further note
416 that this set of functions is by design more robust than necessary.
417 For example, \texttt{writeStructEnd()} is not strictly necessary, as the end of
418 a struct may be implied by the stop field. This method is a convenience for
419 verbose protocols in which it is cleaner to separate these calls (e.g. a closing
420 \texttt{</struct>} tag in XML).
422 \subsection{Structure}
424 Thrift structures are designed to support encoding into a streaming
425 protocol. The implementation should never need to frame or compute the
426 entire data length of a structure prior to encoding it. This is critical to
427 performance in many scenarios. Consider a long list of relatively large
428 strings. If the protocol interface required reading or writing a list to be an
429 atomic operation, then the implementation would need to perform a linear pass over the
430 entire list before encoding any data. However, if the list can be written
431 as iteration is performed, the corresponding read may begin in parallel,
432 theoretically offering an end-to-end speedup of $(kN - C)$, where $N$ is the size
433 of the list, $k$ the cost factor associated with serializing a single
434 element, and $C$ is fixed offset for the delay between data being written
435 and becoming available to read.
437 Similarly, structs do not encode their data lengths a priori. Instead, they are
438 encoded as a sequence of fields, with each field having a type specifier and a
439 unique field identifier. Note that the inclusion of type specifiers allows
440 the protocol to be safely parsed and decoded without any generated code
441 or access to the original IDL file. Structs are terminated by a field header
442 with a special \texttt{STOP} type. Because all the basic types can be read
443 deterministically, all structs (even those containing other structs) can be
444 read deterministically. The Thrift protocol is self-delimiting without any
445 framing and regardless of the encoding format.
447 In situations where streaming is unnecessary or framing is advantageous, it
448 can be very simply added into the transport layer, using the
449 \texttt{TFramedTransport} abstraction.
451 \subsection{Implementation}
453 Facebook has implemented and deployed a space-efficient binary protocol which
454 is used by most backend services. Essentially, it writes all data
455 in a flat binary format. Integer types are converted to network byte order,
456 strings are prepended with their byte length, and all message and field headers
457 are written using the primitive integer serialization constructs. String names
458 for fields are omitted - when using generated code, field identifiers are
459 sufficient.
461 We decided against some extreme storage optimizations (i.e. packing
462 small integers into ASCII or using a 7-bit continuation format) for the sake
463 of simplicity and clarity in the code. These alterations can easily be made
464 if and when we encounter a performance-critical use case that demands them.
466 \section{Versioning}
468 Thrift is robust in the face of versioning and data definition changes. This
469 is critical to enable staged rollouts of changes to deployed services. The
470 system must be able to support reading of old data from log files, as well as
471 requests from out-of-date clients to new servers, and vice versa.
473 \subsection{Field Identifiers}
475 Versioning in Thrift is implemented via field identifiers. The field header
476 for every member of a struct in Thrift is encoded with a unique field
477 identifier. The combination of this field identifier and its type specifier
478 is used to uniquely identify the field. The Thrift definition language
479 supports automatic assignment of field identifiers, but it is good
480 programming practice to always explicitly specify field identifiers.
481 Identifiers are specified as follows:
483 \begin{verbatim}
484 struct Example {
485 1:i32 number=10,
486 2:i64 bigNumber,
487 3:double decimals,
488 4:string name="thrifty"
489 }\end{verbatim}
491 To avoid conflicts between manually and automatically assigned identifiers,
492 fields with identifiers omitted are assigned identifiers
493 decrementing from -1, and the language only supports the manual assignment of
494 positive identifiers.
496 When data is being deserialized, the generated code can use these identifiers
497 to properly identify the field and determine whether it aligns with a field in
498 its definition file. If a field identifier is not recognized, the generated
499 code can use the type specifier to skip the unknown field without any error.
500 Again, this is possible due to the fact that all datatypes are self
501 delimiting.
503 Field identifiers can (and should) also be specified in function argument
504 lists. In fact, argument lists are not only represented as structs on the
505 backend, but actually share the same code in the compiler frontend. This
506 allows for version-safe modification of method parameters
508 \begin{verbatim}
509 service StringCache {
510 void set(1:i32 key, 2:string value),
511 string get(1:i32 key) throws (1:KeyNotFound knf),
512 void delete(1:i32 key)
514 \end{verbatim}
516 The syntax for specifying field identifiers was chosen to echo their structure.
517 Structs can be thought of as a dictionary where the identifiers are keys, and
518 the values are strongly-typed named fields.
520 Field identifiers internally use the \texttt{i16} Thrift type. Note, however,
521 that the \texttt{TProtocol} abstraction may encode identifiers in any format.
523 \subsection{Isset}
525 When an unexpected field is encountered, it can be safely ignored and
526 discarded. When an expected field is not found, there must be some way to
527 signal to the developer that it was not present. This is implemented via an
528 inner \texttt{isset} structure inside the defined objects. (Isset functionality
529 is implicit with a \texttt{null} value in PHP, \texttt{None} in Python
530 and \texttt{nil} in Ruby.) Essentially,
531 the inner \texttt{isset} object of each Thrift struct contains a boolean value
532 for each field which denotes whether or not that field is present in the
533 struct. When a reader receives a struct, it should check for a field being set
534 before operating directly on it.
536 \begin{verbatim}
537 class Example {
538 public:
539 Example() :
540 number(10),
541 bigNumber(0),
542 decimals(0),
543 name("thrifty") {}
545 int32_t number;
546 int64_t bigNumber;
547 double decimals;
548 std::string name;
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;
563 \end{verbatim}
565 \subsection{Case Analysis}
567 There are four cases in which version mismatches may occur.
569 \begin{enumerate}
570 \item \textit{Added field, old client, new server.} In this case, the old
571 client does not send the new field. The new server recognizes that the field
572 is not set, and implements default behavior for out-of-date requests.
573 \item \textit{Removed field, old client, new server.} In this case, the old
574 client sends the removed field. The new server simply ignores it.
575 \item \textit{Added field, new client, old server.} The new client sends a
576 field that the old server does not recognize. The old server simply ignores
577 it and processes as normal.
578 \item \textit{Removed field, new client, old server.} This is the most
579 dangerous case, as the old server is unlikely to have suitable default
580 behavior implemented for the missing field. It is recommended that in this
581 situation the new server be rolled out prior to the new clients.
582 \end{enumerate}
584 \subsection{Protocol/Transport Versioning}
585 The \texttt{TProtocol} abstractions are also designed to give protocol
586 implementations the freedom to version themselves in whatever manner they
587 see fit. Specifically, any protocol implementation is free to send whatever
588 it likes in the \texttt{writeMessageBegin()} call. It is entirely up to the
589 implementor how to handle versioning at the protocol level. The key point is
590 that protocol encoding changes are safely isolated from interface definition
591 version changes.
593 Note that the exact same is true of the \texttt{TTransport} interface. For
594 example, 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
596 data it writes to the file in such a way that it would still accept old
597 log files without the given header.
599 \section{RPC Implementation}
601 \subsection{TProcessor}
603 The last core interface in the Thrift design is the \texttt{TProcessor},
604 perhaps the most simple of the constructs. The interface is as follows:
606 \begin{verbatim}
607 interface TProcessor {
608 bool process(TProtocol in, TProtocol out)
609 throws TException
611 \end{verbatim}
613 The key design idea here is that the complex systems we build can fundamentally
614 be broken down into agents or services that operate on inputs and outputs. In
615 most cases, there is actually just one input and output (an RPC client) that
616 needs handling.
618 \subsection{Generated Code}
620 When a service is defined, we generate a
621 \texttt{TProcessor} instance capable of handling RPC requests to that service,
622 using a few helpers. The fundamental structure (illustrated in pseudo-C++) is
623 as follows:
625 \begin{verbatim}
626 Service.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
635 ServiceHandler.cpp
636 class ServiceHandler : virtual ServiceIf
638 TServer.cpp
639 TServer(TProcessor processor,
640 TServerTransport transport,
641 TTransportFactory tfactory,
642 TProtocolFactory pfactory)
643 serve()
644 \end{verbatim}
646 From the Thrift definition file, we generate the virtual service interface.
647 A client class is generated, which implements the interface and
648 uses two \texttt{TProtocol} instances to perform the I/O operations. The
649 generated processor implements the \texttt{TProcessor} interface. The generated
650 code has all the logic to handle RPC invocations via the \texttt{process()}
651 call, and takes as a parameter an instance of the service interface, as
652 implemented by the application developer.
654 The user provides an implementation of the application interface in separate,
655 non-generated source code.
657 \subsection{TServer}
659 Finally, the Thrift core libraries provide a \texttt{TServer} abstraction.
660 The \texttt{TServer} object generally works as follows.
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
665 transport 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
668 for the \texttt{TTransport}
669 \item Invoke the \texttt{process()} method of the \texttt{TProcessor} object
670 \end{itemize}
672 The layers are appropriately separated such that the server code needs to know
673 nothing about any of the transports, encodings, or applications in play. The
674 server encapsulates the logic around connection handling, threading, etc.
675 while the processor deals with RPC. The only code written by the application
676 developer lives in the definitional Thrift file and the interface
677 implementation.
679 Facebook has deployed multiple \texttt{TServer} implementations, including
680 the single-threaded \texttt{TSimpleServer}, thread-per-connection
681 \texttt{TThreadedServer}, and thread-pooling \texttt{TThreadPoolServer}.
683 The \texttt{TProcessor} interface is very general by design. There is no
684 requirement that a \texttt{TServer} take a generated \texttt{TProcessor}
685 object. Thrift allows the application developer to easily write any type of
686 server that operates on \texttt{TProtocol} objects (for instance, a server
687 could simply stream a certain type of object without any actual RPC method
688 invocation).
690 \section{Implementation Details}
691 \subsection{Target Languages}
692 Thrift currently supports five target languages: C++, Java, Python, Ruby, and
693 PHP. At Facebook, we have deployed servers predominantly in C++, Java, and
694 Python. Thrift services implemented in PHP have also been embedded into the
695 Apache web server, providing transparent backend access to many of our
696 frontend constructs using a \texttt{THttpClient} implementation of the
697 \texttt{TTransport} interface.
699 Though Thrift was explicitly designed to be much more efficient and robust
700 than typical web technologies, as we were designing our XML-based REST web
701 services API we noticed that Thrift could be easily used to define our
702 service interface. Though we do not currently employ SOAP envelopes (in the
703 authors' opinions there is already far too much repetitive enterprise Java
704 software to do that sort of thing), we were able to quickly extend Thrift to
705 generate XML Schema Definition files for our service, as well as a framework
706 for versioning different implementations of our web service. Though public
707 web services are admittedly tangential to Thrift's core use case and design,
708 Thrift facilitated rapid iteration and affords us the ability to quickly
709 migrate our entire XML-based web service onto a higher performance system
710 should the need arise.
712 \subsection{Generated Structs}
713 We made a conscious decision to make our generated structs as transparent as
714 possible. All fields are publicly accessible; there are no \texttt{set()} and
715 \texttt{get()} methods. Similarly, use of the \texttt{isset} object is not
716 enforced. We do not include any \texttt{FieldNotSetException} construct.
717 Developers have the option to use these fields to write more robust code, but
718 the system is robust to the developer ignoring the \texttt{isset} construct
719 entirely and will provide suitable default behavior in all cases.
721 This choice was motivated by the desire to ease application development. Our stated
722 goal is not to make developers learn a rich new library in their language of
723 choice, but rather to generate code that allow them to work with the constructs
724 that are most familiar in each language.
726 We also made the \texttt{read()} and \texttt{write()} methods of the generated
727 objects public so that the objects can be used outside of the context
728 of RPC clients and servers. Thrift is a useful tool simply for generating
729 objects that are easily serializable across programming languages.
731 \subsection{RPC Method Identification}
732 Method calls in RPC are implemented by sending the method name as a string. One
733 issue with this approach is that longer method names require more bandwidth.
734 We experimented with using fixed-size hashes to identify methods, but in the
735 end concluded that the savings were not worth the headaches incurred. Reliably
736 dealing with conflicts across versions of an interface definition file is
737 impossible without a meta-storage system (i.e. to generate non-conflicting
738 hashes for the current version of a file, we would have to know about all
739 conflicts that ever existed in any previous version of the file).
741 We wanted to avoid too many unnecessary string comparisons upon
742 method invocation. To deal with this, we generate maps from strings to function
743 pointers, so that invocation is effectively accomplished via a constant-time
744 hash lookup in the common case. This requires the use of a couple interesting
745 code constructs. Because Java does not have function pointers, process
746 functions are all private member classes implementing a common interface.
748 \begin{verbatim}
749 private class ping implements ProcessFunction {
750 public void process(int seqid,
751 TProtocol iprot,
752 TProtocol oprot)
753 throws TException
754 { ...}
757 HashMap<String,ProcessFunction> processMap_ =
758 new HashMap<String,ProcessFunction>();
759 \end{verbatim}
761 In C++, we use a relatively esoteric language construct: member function
762 pointers.
764 \begin{verbatim}
765 std::map<std::string,
766 void (ExampleServiceProcessor::*)(int32_t,
767 facebook::thrift::protocol::TProtocol*,
768 facebook::thrift::protocol::TProtocol*)>
769 processMap_;
770 \end{verbatim}
772 Using these techniques, the cost of string processing is minimized, and we
773 reap the benefit of being able to easily debug corrupt or misunderstood data by
774 inspecting it for known string method names.
776 \subsection{Servers and Multithreading}
777 Thrift services require basic multithreading to handle simultaneous
778 requests from multiple clients. For the Python and Java implementations of
779 Thrift server logic, the standard threading libraries distributed with the
780 languages provide adequate support. For the C++ implementation, no standard multithread runtime
781 library exists. Specifically, robust, lightweight, and portable
782 thread manager and timer class implementations do not exist. We investigated
783 existing implementations, namely \texttt{boost::thread},
784 \texttt{boost::threadpool}, \texttt{ACE\_Thread\_Manager} and
785 \texttt{ACE\_Timer}.
787 While \texttt{boost::threads}\cite{boost.threads} provides clean,
788 lightweight and robust implementations of multi-thread primitives (mutexes,
789 conditions, threads) it does not provide a thread manager or timer
790 implementation.
792 \texttt{boost::threadpool}\cite{boost.threadpool} also looked promising but
793 was not far enough along for our purposes. We wanted to limit the dependency on
794 third-party libraries as much as possible. Because\\
795 \texttt{boost::threadpool} is
796 not a pure template library and requires runtime libraries and because it is
797 not yet part of the official Boost distribution we felt it was not ready for
798 use in Thrift. As \texttt{boost::threadpool} evolves and especially if it is
799 added to the Boost distribution we may reconsider our decision to not use it.
801 ACE has both a thread manager and timer class in addition to multi-thread
802 primitives. The biggest problem with ACE is that it is ACE. Unlike Boost, ACE
803 API quality is poor. Everything in ACE has large numbers of dependencies on
804 everything else in ACE - thus forcing developers to throw out standard
805 classes, such as STL collections, in favor of ACE's homebrewed implementations. In
806 addition, unlike Boost, ACE implementations demonstrate little understanding
807 of the power and pitfalls of C++ programming and take no advantage of modern
808 templating techniques to ensure compile time safety and reasonable compiler
809 error messages. For all these reasons, ACE was rejected. Instead, we chose
810 to implement our own library, described in the following sections.
812 \subsection{Thread Primitives}
814 The Thrift thread libraries are implemented in the namespace\\
815 \texttt{facebook::thrift::concurrency} and have three components:
816 \begin{itemize}
817 \item primitives
818 \item thread pool manager
819 \item timer manager
820 \end{itemize}
822 As mentioned above, we were hesitant to introduce any additional dependencies
823 on Thrift. We decided to use \texttt{boost::shared\_ptr} because it is so
824 useful for multithreaded application, it requires no link-time or
825 runtime libraries (i.e. it is a pure template library) and it is due
826 to become part of the C++0x standard.
828 We implement standard \texttt{Mutex} and \texttt{Condition} classes, and a
829 \texttt{Monitor} class. The latter is simply a combination of a mutex and
830 condition variable and is analogous to the \texttt{Monitor} implementation provided for
831 the Java \texttt{Object} class. This is also sometimes referred to as a barrier. We
832 provide a \texttt{Synchronized} guard class to allow Java-like synchronized blocks.
833 This is just a bit of syntactic sugar, but, like its Java counterpart, clearly
834 delimits critical sections of code. Unlike its Java counterpart, we still
835 have the ability to programmatically lock, unlock, block, and signal monitors.
837 \begin{verbatim}
838 void run() {
839 {Synchronized s(manager->monitor);
840 if (manager->state == TimerManager::STARTING) {
841 manager->state = TimerManager::STARTED;
842 manager->monitor.notifyAll();
846 \end{verbatim}
848 We again borrowed from Java the distinction between a thread and a runnable
849 class. A \texttt{Thread} is the actual schedulable object. The
850 \texttt{Runnable} is the logic to execute within the thread.
851 The \texttt{Thread} implementation deals with all the platform-specific thread
852 creation and destruction issues, while the \texttt{Runnable} implementation deals
853 with the application-specific per-thread logic. The benefit of this approach
854 is that developers can easily subclass the Runnable class without pulling in
855 platform-specific super-classes.
857 \subsection{Thread, Runnable, and shared\_ptr}
858 We use \texttt{boost::shared\_ptr} throughout the \texttt{ThreadManager} and
859 \texttt{TimerManager} implementations to guarantee cleanup of dead objects that can
860 be 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
863 creating and shutting down threads.
865 Thread creation requires calling into a C library. (In our case the POSIX
866 thread library, \texttt{libpthread}, but the same would be true for WIN32 threads).
867 Typically, the OS makes few, if any, guarantees about when \texttt{ThreadMain}, a C thread's entry-point function, will be called. Therefore, it is
868 possible that our thread create call,
869 \texttt{ThreadFactory::newThread()} could return to the caller
870 well before that time. To ensure that the returned \texttt{Thread} object is not
871 prematurely cleaned up if the caller gives up its reference prior to the
872 \texttt{ThreadMain} call, the \texttt{Thread} object makes a weak referenence to
873 itself in its \texttt{start} method.
875 With the weak reference in hand the \texttt{ThreadMain} function can attempt to get
876 a strong reference before entering the \texttt{Runnable::run} method of the
877 \texttt{Runnable} object bound to the \texttt{Thread}. If no strong references to the
878 thread are obtained between exiting \texttt{Thread::start} and entering \texttt{ThreadMain}, the weak reference returns \texttt{null} and the function
879 exits immediately.
881 The need for the \texttt{Thread} to make a weak reference to itself has a
882 significant impact on the API. Since references are managed through the
883 \texttt{boost::shared\_ptr} templates, the \texttt{Thread} object must have a reference
884 to itself wrapped by the same \texttt{boost::shared\_ptr} envelope that is returned
885 to 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
888 implementing the \texttt{Thread} interface (in this case, \texttt{PosixThread::weakRef})
889 to allow it to make add weak reference to itself through the
890 \texttt{boost::shared\_ptr} envelope.
892 \texttt{Thread} and \texttt{Runnable} objects reference each other. A \texttt{Runnable}
893 object may need to know about the thread in which it is executing, and a Thread, obviously,
894 needs to know what \texttt{Runnable} object it is hosting. This interdependency is
895 further complicated because the lifecycle of each object is independent of the
896 other. 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
897 once a thread has been created and started for it.
899 The \texttt{Thread} class takes a \texttt{boost::shared\_ptr} reference to the hosted
900 \texttt{Runnable} object in its constructor, while the \texttt{Runnable} class has an
901 explicit \texttt{thread} method to allow explicit binding of the hosted thread.
902 \texttt{ThreadFactory::newThread} binds the objects to each other.
904 \subsection{ThreadManager}
906 \texttt{ThreadManager} creates a pool of worker threads and
907 allows applications to schedule tasks for execution as free worker threads
908 become available. The \texttt{ThreadManager} does not implement dynamic
909 thread pool resizing, but provides primitives so that applications can add
910 and remove threads based on load. This approach was chosen because
911 implementing load metrics and thread pool size is very application
912 specific. For example some applications may want to adjust pool size based
913 on running-average of work arrival rates that are measured via polled
914 samples. Others may simply wish to react immediately to work-queue
915 depth high and low water marks. Rather than trying to create a complex
916 API abstract enough to capture these different approaches, we
917 simply leave it up to the particular application and provide the
918 primitives to enact the desired policy and sample current status.
920 \subsection{TimerManager}
922 \texttt{TimerManager} allows applications to schedule
923 \texttt{Runnable} objects for execution at some point in the future. Its specific task
924 is to allows applications to sample \texttt{ThreadManager} load at regular
925 intervals and make changes to the thread pool size based on application policy.
926 Of course, it can be used to generate any number of timer or alarm events.
928 The default implementation of \texttt{TimerManager} uses a single thread to
929 execute expired \texttt{Runnable} objects. Thus, if a timer operation needs to
930 do a large amount of work and especially if it needs to do blocking I/O,
931 that should be done in a separate thread.
933 \subsection{Nonblocking Operation}
934 Though the Thrift transport interfaces map more directly to a blocking I/O
935 model, we have implemented a high performance \texttt{TNonBlockingServer}
936 in C++ based on \texttt{libevent} and the \texttt{TFramedTransport}. We
937 implemented this by moving all I/O into one tight event loop using a
938 state machine. Essentially, the event loop reads framed requests into
939 \texttt{TMemoryBuffer} objects. Once entire requests are ready, they are
940 dispatched to the \texttt{TProcessor} object which can read directly from
941 the data in memory.
943 \subsection{Compiler}
944 The Thrift compiler is implemented in C++ using standard \texttt{lex}/\texttt{yacc}
945 lexing and parsing. Though it could have been implemented with fewer
946 lines of code in another language (i.e. Python Lex-Yacc (PLY) or \texttt{ocamlyacc}), using C++
947 forces explicit definition of the language constructs. Strongly typing the
948 parse tree elements (debatably) makes the code more approachable for new
949 developers.
951 Code generation is done using two passes. The first pass looks only for
952 include files and type definitions. Type definitions are not checked during
953 this phase, since they may depend upon include files. All included files
954 are sequentially scanned in a first pass. Once the include tree has been
955 resolved, a second pass over all files is taken that inserts type definitions
956 into the parse tree and raises an error on any undefined types. The program is
957 then generated against the parse tree.
959 Due to inherent complexities and potential for circular dependencies,
960 we explicitly disallow forward declaration. Two Thrift structs cannot
961 each contain an instance of the other. (Since we do not allow \texttt{null}
962 struct instances in the generated C++ code, this would actually be impossible.)
964 \subsection{TFileTransport}
965 The \texttt{TFileTransport} logs Thrift requests/structs by
966 framing incoming data with its length and writing it out to disk.
967 Using a framed on-disk format allows for better error checking and
968 helps with the processing of a finite number of discrete events. The\\
969 \texttt{TFileWriterTransport} uses a system of swapping in-memory buffers
970 to ensure good performance while logging large amounts of data.
971 A Thrift log file is split up into chunks of a specified size; logged messages
972 are not allowed to cross chunk boundaries. A message that would cross a chunk
973 boundary will cause padding to be added until the end of the chunk and the
974 first byte of the message are aligned to the beginning of the next chunk.
975 Partitioning the file into chunks makes it possible to read and interpret data
976 from a particular point in the file.
978 \section{Facebook Thrift Services}
979 Thrift has been employed in a large number of applications at Facebook, including
980 search, logging, mobile, ads and the developer platform. Two specific usages are discussed below.
982 \subsection{Search}
983 Thrift is used as the underlying protocol and transport layer for the Facebook Search service.
984 The multi-language code generation is well suited for search because it allows for application
985 development in an efficient server side language (C++) and allows the Facebook PHP-based web application
986 to make calls to the search service using Thrift PHP libraries. There is also a large
987 variety of search stats, deployment and testing functionality that is built on top
988 of generated Python code. Additionally, the Thrift log file format is
989 used as a redo log for providing real-time search index updates. Thrift has allowed the
990 search team to leverage each language for its strengths and to develop code at a rapid pace.
992 \subsection{Logging}
993 The Thrift \texttt{TFileTransport} functionality is used for structured logging. Each
994 service function definition along with its parameters can be considered to be
995 a structured log entry identified by the function name. This log can then be used for
996 a variety of purposes, including inline and offline processing, stats aggregation and as a redo log.
998 \section{Conclusions}
999 Thrift has enabled Facebook to build scalable backend
1000 services efficiently by enabling engineers to divide and conquer. Application
1001 developers can focus on application code without worrying about the
1002 sockets layer. We avoid duplicated work by writing buffering and I/O logic
1003 in one place, rather than interspersing it in each application.
1005 Thrift has been employed in a wide variety of applications at Facebook,
1006 including search, logging, mobile, ads, and the developer platform. We have
1007 found that the marginal performance cost incurred by an extra layer of
1008 software abstraction is far eclipsed by the gains in developer efficiency and
1009 systems reliability.
1011 \appendix
1013 \section{Similar Systems}
1014 The following are software systems similar to Thrift. Each is (very!) briefly
1015 described:
1017 \begin{itemize}
1018 \item \textit{SOAP.} XML-based. Designed for web services via HTTP, excessive
1019 XML parsing overhead.
1020 \item \textit{CORBA.} Relatively comprehensive, debatably overdesigned and
1021 heavyweight. Comparably cumbersome software installation.
1022 \item \textit{COM.} Embraced mainly in Windows client softare. Not an entirely
1023 open solution.
1024 \item \textit{Pillar.} Lightweight and high-performance, but missing versioning
1025 and abstraction.
1026 \item \textit{Protocol Buffers.} Closed-source, owned by Google. Described in
1027 Sawzall paper.
1028 \end{itemize}
1030 \acks
1032 Many thanks for feedback on Thrift (and extreme trial by fire) are due to
1033 Martin Smith, Karl Voskuil and Yishan Wong.
1035 Thrift is a successor to Pillar, a similar system developed
1036 by Adam D'Angelo, first while at Caltech and continued later at Facebook.
1037 Thrift simply would not have happened without Adam's insights.
1039 \begin{thebibliography}{}
1041 \bibitem{boost.threads}
1042 Kempf, William,
1043 ``Boost.Threads'',
1044 \url{http://www.boost.org/doc/html/threads.html}
1046 \bibitem{boost.threadpool}
1047 Henkel, Philipp,
1048 ``threadpool'',
1049 \url{http://threadpool.sourceforge.net}
1051 \end{thebibliography}
1053 \end{document}