wealthfront's query engine
DESCRIPTION
Presentation of Wealthfront's Service Framework and Standardized RPC given at Square in March 2012. The majority of the slides comes from an internal presentation I gave in February 2011.TRANSCRIPT
Roadmap
• Query Engine • Queries • Services • Remote Query InvocaKon
The Query Engine is
• a plaMorm to build distributed services quickly
• designed with testability in mind • powering all of Wealthfront’s backend services • running on the JVM
The Query Engine is not
• a web framework
• a RESTful web service – a query is a funcKon, not a resource
• Java-‐specific
Queries
• First-‐class ciKzens – Queries can be passed around and later invoked
• Serializable – Queries can be persisted
• Closed with their dependencies • Composable units of work
– Queries can invoke other queries – Queries can produce other queries
• Entry points into our backend services – Services can invoke queries on other services – Queries can also be invoked from the command line
Queries (concretely)
• Queries are classes • Constructors define input parameters • Instances are invokable using a driver • Dependencies can be requested at run-‐Kme
• InvocaKons produce a result • Queries are easy to test
UNIX Processes
Process
stdin Environment
stdout Return code
stderr
Queries
Query
Arguments Dependencies
Result ExcepKon
Invoking a Query
Query Class
Driver
Query Instance
Arguments
Dependencies
Result
Result
Run
Query Instance
Invoking a Query
Scoping Monitoring Retrying TransacKng InjecKng
Dependencies
Services
• A collecKon of Queries – Usually with a similar purpose, e.g. all the queries related to customer management
• Able to saKsfy the dependencies required by its Queries – E.g. access to the customer database, connecKon to the NASDAQ NLS feed, …
• Queries can be installed in different services
Remote Query InvocaKon
• Queries are remotely invoked by doing an HTTP POST request
• Arguments are encoded in the HTTP request
• Results are returned in the HTTP response
Request SerializaKon
• Most of our services rely on the so-‐called “qp0p1” serializaKon – The simple name of the invoked query is passed with the q parameter
– The nth argument is passed with the pnth parameter
– Arguments are serialized and de-‐serialized to and from strings using converters
Hello(@OpKonal(“World”) String): String
Instan&a&on Serializa&on
new Hello(“Bob”) q=Hello&p0=Bob
new Hello(null) q=Hello
[julien@glados ~]$ curl um0:8085 -‐-‐data 'q=Hello&p0=Bob’ -‐v
> POST / HTTP/1.1> Content-‐Length: 14 > Content-‐Type: applicaKon/x-‐www-‐form-‐urlencoded
> > q=Hello&p0=Bob
< HTTP/1.1 200 OK < X-‐KC-‐TraceToken: 962dcf36-‐9b25-‐4731-‐a022-‐e054b925637c
< Server: kawala < Content-‐Length: 11
Hello, Bob!
Request De-‐serializaKon
• Services’ request interpreters de-‐serialize the HTTP POST requests and create instances of the query class using the specified arguments
• Then, they invoke the query instance using a driver
Request De-‐serializaKon
Query Class
Request Interpreter
Query Instance
Arguments
HTTP POST request
Driver
Smart Clients
• Queries can be remotely invoked from Java code using smart clients
• Smart clients analyze the bytecode to recover the arguments passed to the query constructor and generate the HTTP POST request
• Smart clients also de-‐serialize the result from the HTTP response (see “Result SerializaKon”)
Smart Clients
Query Class
Smart Client
Query Instance
Arguments
HTTP POST request
The Life of a Remote Request
Query Class
Smart Client
Query Instance
Arguments
HTTP POST request
Query Class
Request Interpreter
Query Instance
Arguments
Driver
Client Server
Constructor Requirements
• In order to be analyzable, query constructors must follow strict requirements – Basically, they should only assign their arguments to fields
hwps://github.com/wealthfront/kawala/wiki/InstanKators
Sugar for the Constructor
• @OpKonal(“2011-‐02-‐25”) LocalDate • @OpKonal(“false”) boolean
• OpKon<LocalDate> • @PosiKve int age
Early ValidaKon
• Arguments are validated when instanKaKng a serialized query thanks to the converters
[julien@glados ~]$ ikq um0 GetUser foo
HTTP Error 400: For input string: "foo” X-‐KC-‐TraceToken: 962dcf36-‐9b25-‐4731-‐a022-‐e054b925637c
Server: kawala ConnecKon: close
Result SerializaKon
• Results are usually serialized as – JSON – Protobuf
• The service has the responsibility to serialize the result of a query with the proper serializaKon method
• Smart clients rely on the same logic to select the proper de-‐serializaKon method
Result SerializaKon
Return Type Addi&onal Constraints Serializa&on
ConverKble type* JSON value**
T T annotated with @EnKty JSON object
List<T> T annotated with @EnKty JSON array
Set<T> T annotated with @EnKty JSON array
T extends Message Protobuf
List<T extends Message> Protobuf array
* As specified in KachingMarshallers ** JSON strings are returned without surrounding quotes
The Life of a Remote InvocaKon
Query Class
Smart Client
Query Instance
Arguments Query Class
Request Interpreter
Query Instance
Arguments
Driver
Client Server
Result
Serializer
Result
Query InvocaKon Summary
• Local InvocaKon QueryExecutor executor = …
User user = executor.submit(new GetUser(Id.<User> of(10));
• Remote InvocaKon SmartClient<UM> um = … User user = um.invoke(new GetUser(Id.<User> of(10));