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