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