Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 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
% You will need a copy of sigplanconf.cls to format this document.
12
% It is available at <http://www.sigplan.org/authorInformation.htm>.
13
%
14
%-----------------------------------------------------------------------------
15
 
16
 
17
\documentclass[nocopyrightspace,blockstyle]{sigplanconf}
18
 
19
\usepackage{amssymb}
20
\usepackage{amsfonts}
21
\usepackage{amsmath}
22
\usepackage{url}
23
 
24
\begin{document}
25
 
26
% \conferenceinfo{WXYZ '05}{date, City.}
27
% \copyrightyear{2007}
28
% \copyrightdata{[to be supplied]}
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
49
define datatypes and service interfaces in a single language-neutral file
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
68
demands of many operations on the site (i.e. search,
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
75
tools and implementations available over standardizing on any one
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
81
sufficient datatype freedom, or suffered from subpar performance.
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
89
validated code that can be run without the need for
90
any advanced introspective run-time type checking. It is also designed to
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
103
without requiring that the application developer use custom Thrift datatypes
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
108
to achieve this. Section 2 details the Thrift type system.
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
 
117
\textit{Protocol.} Datatypes must have some way of using the Transport
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
 
124
\textit{Versioning.} For robust services, the involved datatypes must
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
132
streams to accomplish remote procedure calls. Section 6 details the generated
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
144
any code for object serialization or transport. The Thrift IDL (Interface
145
Definition Language) file is
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
154
on the key types available in all programming languages, ommitting any
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
166
\item \texttt{binary} A byte array representation for blobs
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
 
180
\subsection{Structs}
181
 
182
A Thrift struct defines a common object to be used across languages. A struct
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
 
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
202
dictionary in PHP/Ruby.
203
\item \texttt{map<type1,type2>} A map of strictly unique keys to values
204
Translates into an STL \texttt{map}, Java \texttt{HashMap}, PHP associative
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
 
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
235
in each target programming language, in order to seamlessly
236
integrate with native exception handling in any given
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
243
semantically equivalent to defining an interface (or a pure virtual abstract
244
class) in object oriented
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),
261
  void delete(1:i32 key)
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
267
keyword may be added to a \texttt{void} function, which will generate code that does
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
271
the client will only be guaranteed that the request succeeded at the
272
transport layer. (In many transport scenarios this is inherently unreliable
273
due to the Byzantine Generals' Problem. Therefore, application developers
274
should take care only to use the async optimization in cases where dropped
275
method calls are acceptable or the transport is known to be reliable.)
276
 
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.
280
 
281
\section{Transport}
282
 
283
The transport layer is used by the generated code to facilitate data transfer.
284
 
285
\subsection{Interface}
286
 
287
A key design choice in the implementation of Thrift was to decouple the
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
290
communication, there was no compelling reason to build that constraint into
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
 
296
Fundamentally, generated Thrift code only needs to know how to read and
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:
300
 
301
\begin{itemize}
302
\item \texttt{open} Opens the tranpsort
303
\item \texttt{close} Closes the tranport
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
308
\end{itemize}
309
 
310
There are a few additional methods not documented here which are used to aid
311
in batching reads and optionally signaling the completion of a read or
312
write operation from the generated code.
313
 
314
In addition to the above
315
\texttt{TTransport} interface, there is a\\
316
\texttt{TServerTransport} interface
317
used to accept or create primitive transport objects. Its interface is as
318
follows:
319
 
320
\begin{itemize}
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
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
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.
344
 
345
\subsubsection{Utilities}
346
 
347
The Transport interface is designed to support easy extension using common
348
OOP techniques, such as composition. Some simple utilites include the
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.
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,
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.
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
 
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
415
that signals the end of a struct. The procedure for reading a struct is to
416
\texttt{readFieldBegin()} until the stop field is encountered, and then to
417
\texttt{readStructEnd()}.  The
418
generated code relies upon this call sequence to ensure that everything written by
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
423
verbose protocols in which it is cleaner to separate these calls (e.g. a closing
424
\texttt{</struct>} tag in XML).
425
 
426
\subsection{Structure}
427
 
428
Thrift structures are designed to support encoding into a streaming
429
protocol. The implementation should never need to frame or compute the
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
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
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,
436
theoretically offering an end-to-end speedup of $(kN - C)$, where $N$ is the size
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
443
unique field identifier. Note that the inclusion of type specifiers allows
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
447
deterministically, all structs (even those containing other structs) can be
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
468
if and when we encounter a performance-critical use case that demands them.
469
 
470
\section{Versioning}
471
 
472
Thrift is robust in the face of versioning and data definition changes. This
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.
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
 
495
To avoid conflicts between manually and automatically assigned identifiers,
496
fields with identifiers omitted are assigned identifiers
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.
504
Again, this is possible due to the fact that all datatypes are self
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),
516
  void delete(1:i32 key)
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
522
the values are strongly-typed named fields.
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
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
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),
547
    name("thrifty") {}
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
}
567
\end{verbatim}
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
576
is not set, and implements default behavior for out-of-date requests.
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
601
log files without the given header.
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
 
650
From the Thrift definition file, we generate the virtual service interface.
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()}
655
call, and takes as a parameter an instance of the service interface, as
656
implemented by the application developer.
657
 
658
The user provides an implementation of the application interface in separate,
659
non-generated source code.
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
680
developer lives in the definitional Thrift file and the interface
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
707
authors' opinions there is already far too much repetitive enterprise Java
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
714
should the need arise.
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
 
725
This choice was motivated by the desire to ease application development. Our stated
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
731
objects public so that the objects can be used outside of the context
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
778
inspecting it for known string method names.
779
 
780
\subsection{Servers and Multithreading}
781
Thrift services require basic multithreading to handle simultaneous
782
requests from multiple clients. For the Python and Java implementations of
783
Thrift server logic, the standard threading libraries distributed with the
784
languages provide adequate support. For the C++ implementation, no standard multithread runtime
785
library exists. Specifically, robust, lightweight, and portable
786
thread manager and timer class implementations do not exist. We investigated
787
existing implementations, namely \texttt{boost::thread},
788
\texttt{boost::threadpool}, \texttt{ACE\_Thread\_Manager} and
789
\texttt{ACE\_Timer}.
790
 
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
794
implementation.
795
 
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
798
third-party libraries as much as possible. Because\\
799
\texttt{boost::threadpool} is
800
not a pure template library and requires runtime libraries and because it is
801
not yet part of the official Boost distribution we felt it was not ready for
802
use in Thrift. As \texttt{boost::threadpool} evolves and especially if it is
803
added to the Boost distribution we may reconsider our decision to not use it.
804
 
805
ACE has both a thread manager and timer class in addition to multi-thread
806
primitives. The biggest problem with ACE is that it is ACE. Unlike Boost, ACE
807
API quality is poor. Everything in ACE has large numbers of dependencies on
808
everything else in ACE - thus forcing developers to throw out standard
809
classes, such as STL collections, in favor of ACE's homebrewed implementations. In
810
addition, unlike Boost, ACE implementations demonstrate little understanding
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
813
error messages. For all these reasons, ACE was rejected. Instead, we chose
814
to implement our own library, described in the following sections.
815
 
816
\subsection{Thread Primitives}
817
 
818
The Thrift thread libraries are implemented in the namespace\\
819
\texttt{facebook::thrift::concurrency} and have three components:
820
\begin{itemize}
821
\item primitives
822
\item thread pool manager
823
\item timer manager
824
\end{itemize}
825
 
826
As mentioned above, we were hesitant to introduce any additional dependencies
827
on Thrift. We decided to use \texttt{boost::shared\_ptr} because it is so
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.
831
 
832
We implement standard \texttt{Mutex} and \texttt{Condition} classes, and a
833
 \texttt{Monitor} class. The latter is simply a combination of a mutex and
834
condition variable and is analogous to the \texttt{Monitor} implementation provided for
835
the Java \texttt{Object} class. This is also sometimes referred to as a barrier. We
836
provide a \texttt{Synchronized} guard class to allow Java-like synchronized blocks.
837
This is just a bit of syntactic sugar, but, like its Java counterpart, clearly
838
delimits critical sections of code. Unlike its Java counterpart, we still
839
have the ability to programmatically lock, unlock, block, and signal monitors.
840
 
841
\begin{verbatim}
842
void run() {
843
 {Synchronized s(manager->monitor);
844
  if (manager->state == TimerManager::STARTING) {
845
    manager->state = TimerManager::STARTED;
846
    manager->monitor.notifyAll();
847
  }
848
 }
849
}
850
\end{verbatim}
851
 
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
854
\texttt{Runnable} is the logic to execute within the thread.
855
The \texttt{Thread} implementation deals with all the platform-specific thread
856
creation and destruction issues, while the \texttt{Runnable} implementation deals
857
with the application-specific per-thread logic. The benefit of this approach
858
is that developers can easily subclass the Runnable class without pulling in
859
platform-specific super-classes.
860
 
861
\subsection{Thread, Runnable, and shared\_ptr}
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
867
creating and shutting down threads.
868
 
869
Thread creation requires calling into a C library. (In our case the POSIX
870
thread library, \texttt{libpthread}, but the same would be true for WIN32 threads).
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
872
possible that our thread create call,
873
\texttt{ThreadFactory::newThread()} could return to the caller
874
well before that time. To ensure that the returned \texttt{Thread} object is not
875
prematurely cleaned up if the caller gives up its reference prior to the
876
\texttt{ThreadMain} call, the \texttt{Thread} object makes a weak referenence to
877
itself in its \texttt{start} method.
878
 
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
881
\texttt{Runnable} object bound to the \texttt{Thread}. If no strong references to the
882
thread are obtained between exiting \texttt{Thread::start} and entering \texttt{ThreadMain}, the weak reference returns \texttt{null} and the function
883
exits immediately.
884
 
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
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})
893
 to allow it to make add weak reference to itself through the
894
 \texttt{boost::shared\_ptr} envelope.
895
 
896
\texttt{Thread} and \texttt{Runnable} objects reference each other. A \texttt{Runnable}
897
object may need to know about the thread in which it is executing, and a Thread, obviously,
898
needs to know what \texttt{Runnable} object it is hosting. This interdependency is
899
further complicated because the lifecycle of each object is independent of the
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
901
once a thread has been created and started for it.
902
 
903
The \texttt{Thread} class takes a \texttt{boost::shared\_ptr} reference to the hosted
904
\texttt{Runnable} object in its constructor, while the \texttt{Runnable} class has an
905
explicit \texttt{thread} method to allow explicit binding of the hosted thread.
906
\texttt{ThreadFactory::newThread} binds the objects to each other.
907
 
908
\subsection{ThreadManager}
909
 
910
\texttt{ThreadManager} creates a pool of worker threads and
911
allows applications to schedule tasks for execution as free worker threads
912
become available. The \texttt{ThreadManager} does not implement dynamic
913
thread pool resizing, but provides primitives so that applications can add
914
and remove threads based on load. This approach was chosen because
915
implementing load metrics and thread pool size is very application
916
specific. For example some applications may want to adjust pool size based
917
on running-average of work arrival rates that are measured via polled
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
920
API abstract enough to capture these different approaches, we
921
simply leave it up to the particular application and provide the
922
primitives to enact the desired policy and sample current status.
923
 
924
\subsection{TimerManager}
925
 
926
\texttt{TimerManager} allows applications to schedule
927
 \texttt{Runnable} objects for execution at some point in the future. Its specific task
928
is to allows applications to sample \texttt{ThreadManager} load at regular
929
intervals and make changes to the thread pool size based on application policy.
930
Of course, it can be used to generate any number of timer or alarm events.
931
 
932
The default implementation of \texttt{TimerManager} uses a single thread to
933
execute expired \texttt{Runnable} objects. Thus, if a timer operation needs to
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.
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}
940
in C++ based on \texttt{libevent} and the \texttt{TFramedTransport}. We
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}
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++
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
959
resolved, a second pass over all files is taken that inserts type definitions
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
 
968
\subsection{TFileTransport}
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
972
helps with the processing of a finite number of discrete events. The\\
973
\texttt{TFileWriterTransport} uses a system of swapping in-memory buffers
974
to ensure good performance while logging large amounts of data.
975
A Thrift log file is split up into chunks of a specified size; logged messages
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
978
first byte of the message are aligned to the beginning of the next chunk.
979
Partitioning the file into chunks makes it possible to read and interpret data
980
from a particular point in the file.
981
 
982
\section{Facebook Thrift Services}
983
Thrift has been employed in a large number of applications at Facebook, including
984
search, logging, mobile, ads and the developer platform. Two specific usages are discussed below.
985
 
986
\subsection{Search}
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
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
991
variety of search stats, deployment and testing functionality that is built on top
992
of generated Python code. Additionally, the Thrift log file format is
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.
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
999
a structured log entry identified by the function name. This log can then be used for
1000
a variety of purposes, including inline and offline processing, stats aggregation and as a redo log.
1001
 
1002
\section{Conclusions}
1003
Thrift has enabled Facebook to build scalable backend
1004
services efficiently by enabling engineers to divide and conquer. Application
1005
developers can focus on application code without worrying about the
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,
1010
including search, logging, mobile, ads, and the developer platform. We have
1011
found that the marginal performance cost incurred by an extra layer of
1012
software abstraction is far eclipsed by the gains in developer efficiency and
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.
1026
\item \textit{COM.} Embraced mainly in Windows client softare. Not an entirely
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
1037
Martin Smith, Karl Voskuil and Yishan Wong.
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
 
1043
\begin{thebibliography}{}
1044
 
1045
\bibitem{boost.threads}
1046
Kempf, William,
1047
``Boost.Threads'',
1048
\url{http://www.boost.org/doc/html/threads.html}
1049
 
1050
\bibitem{boost.threadpool}
1051
Henkel, Philipp,
1052
``threadpool'',
1053
\url{http://threadpool.sourceforge.net}
1054
 
1055
\end{thebibliography}
1056
 
1057
\end{document}