Created
May 27, 2015 18:44
-
-
Save mfunkie/65e94f42fee3962b1703 to your computer and use it in GitHub Desktop.
Relay Talk Transcription by NPM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>> I work on products infrastructure team at | |
Facebook, and back in January at react day two of my | |
teammates Dan safer gave us introducing relay, which is | |
a data stream work. So today in this talk, going to | |
start with a description of relay for anyone who didn't | |
see this or who needs an infrastructure, and then I'm | |
going to dive into specific cards of the relay. | |
So let's start by thinking about how we at | |
Facebook were dining inclines a year ago. At that point | |
we had developed react, and which also buys a pattern | |
for one way flow flew application. And when we use both | |
inflow and react, we found we can move faster through | |
the process. But there was one big problem that neither | |
react nor flux really addressed. And that almost every | |
signs has faced at one time or another, which was what's | |
the best way to fetch data from the server and to | |
organize that data. | |
So let's look at how data fetching might work | |
in a application built with reaction. And I'm going to | |
use this example of a news feed story that I wrote. And | |
in particular it's likes and comments actually on the | |
bottom. So this like and comment box is -- was one of | |
the first parts of Facebook that we built with reaction | |
flux. It's a really big part of Facebook where you get | |
people from a bunch of different peoples coming in which | |
changing it frequently, so it's kind of a hot part of | |
Facebook. | |
So let's consider the example of introducing | |
stickers and comments, this is something that we | |
actually did last fall. Let's say that -- and this part | |
of the application our component tree consists of the | |
comment Bobs, which contains a comment list, which | |
contains a series of comment items. | |
So how did each of these components get the | |
data that it needs? The data comes from the server, and | |
then it will get passed down through the component tree. | |
So comment box is going to get some set of data and then | |
it will take a subset of that and pass it into comment | |
list. And then comment list will take Senate Bill you | |
sets of comment data and base past it into each item. | |
In this model, the most important thing to | |
notice is each is going to need to be aware so that it | |
can pass the data down. And then that server needs to | |
have the data required by every single component. | |
So, in other words, the implementations detail | |
of each component, the detail of what each component | |
needs are leaked up to the parents and for the | |
[Indiscernible]. Why does this matter? | |
Well, it means if you want to come along and | |
introduce sticker comments, you're also going to need to | |
change the comment list, the comment Bobs, and that | |
server end point. This is not cool when you need to | |
change all those files, especially when you have | |
different people coming in all the time making changes | |
and wenting to modify all the time. It turns into a | |
slower comment process. | |
And it can get pretty confusing when you have | |
the data fetch on the server. So let's say that you | |
look at your server end point, and you see that you're | |
effecting the birthdays. It's really hard to know at a | |
advance whether and where this birthday data is being | |
used in your application, on the client. So maybe | |
someone ran a test a few months ago and they wanted to | |
see what would happen if they wanted to add birthdays | |
for commentators, it didn't work that well, so they | |
removed that. | |
And now we have this over fetching situation | |
where we're loading that birthday, sending it to the | |
client, and then we're not doing anything with it, so | |
it's just wasteful. So maybe we're going to try to do a | |
good deal deed and clean up the server, remove that | |
birthday code without realizing there is a corner of the | |
application where we're still using the birthdays, and | |
now we have an under fetching bug, so we're going to | |
such the birthday and the things won't be there. | |
It's pretty easy to put these data fetching | |
pieces to get out of things like this and it will often | |
cause you over fetching or under fetching. | |
So how would this work in a perfect world? | |
Ideal if you wanted to introduce sticker comments, we | |
would just that need component. And more logically, it | |
would reside in just one place. The same place where we | |
do our rendering. This is the idea of the forward | |
relay. That's to make the development easier, we should | |
keep our data fetching and rendering in the same place. | |
Namely within the react component. | |
So rather than just continue logic render | |
itself like a transitional reaction component, a relay | |
component also contains a declaration of data that it | |
needs in the form of the data query. This way when | |
someone needs to make a small change, they can just find | |
the developing component, change the data theory, and | |
then they're done. They don't need to change the | |
components or touch the server. With the data acquirey | |
and the refreshing in one place witness it's easier to | |
know when you're finding the data that you didn't fetch. | |
So you're less likely to get that over fetching or under | |
fetching. | |
In order to achieve all these benefits of | |
putting the data queer into the component, you need a | |
common way for the components to declare their data | |
requirements, and this is where the FQL comes in. For | |
the last couple of years, our applications at Facebook | |
have been using it to set their needs. Let's look at an | |
example to see what it might look look like. | |
So let's look at the data that we might need | |
to render a comment off of here. | |
So we need the ID of the person, the person's | |
name, and then some data we need to render the profile | |
picture. So let's see what happens if we ahighlight the | |
field here and remove the values. | |
As you can see the query here not only | |
expressing the data that it needs, but also the precise | |
infrastructure that we want the response to that query | |
to have. The SQL is designed to be a thin layer so that | |
clients can get it without the whole server needing to | |
be rewritten. Another important feature of SQL is that | |
you can compose the theory, so, basically, one is built | |
up of others. | |
So this mean that if you take a component like | |
this application. On the one hand you can look at it to | |
build a view for the whole application. So each one | |
will render. In the same way, you can take the | |
component tree and look at it as a guide for how to big | |
a stroke for the application. Each parent component for | |
the SQL query is composed from the subsequently. | |
Each component contains a theory and a render | |
method. It's going to take the component if each method | |
and send that to the server. The server is going to | |
respond, and then relay puts our response data into a | |
single store, and then it will use that data to | |
construct top and then send it out to the corresponds. | |
At it's core, this is a flux application, the | |
only difference is it has this one single store to use | |
generic logic. Having a single store has a number of | |
nice benefits. So for one it reduces the need for a lot | |
of the flux boilerplate. It also helps the data | |
consistency across different parts of the application. | |
And finally it let's us build certain comment patterns. | |
Something that you guys have probably done at least | |
once, right into relay so that people don't need to | |
build them every time. | |
So now that we see on you relay works, let's | |
go back to this sticker sample and see how much easier | |
relay is going to make our lives. We just change | |
comment item. And more specifically let's say this is | |
part of our SQL theory, right now we're just doing the | |
test of the comment, and now we can also ask the | |
sticker, use that in our render message, and we're done. | |
We can build other stuff in the time had a we would have | |
spent changing those other files. | |
So relay resolves this big problem on how to | |
fetch data in a way that we found nicely for | |
applications being built with a big engineering team. | |
So the when you have those in the component, it's easy | |
for a lot of people to work on a lot of different parts | |
of an application at once. | |
So I can make my changes, my teammates can | |
make their changes, people on other teams can make their | |
changes, and we don't need to worry whether or not we're | |
stepping on owe each other's toes and what they're doing | |
at that exact time. | |
So far we've talked about the read passing | |
relay. But it would have limited usefulness. It give | |
users away to take action. Like, make making a story, | |
or liking my own story. So I have the term mutation so | |
far these actions are in a application. | |
In Facebook this would be something, like, | |
sharing a link, or poking a friend. And for the rest of | |
the talk, I'll focus on homotaxes energetic real life. | |
So to start, I'm going to go back and talk | |
about how we at Facebook are building these before | |
relay. So I joined Facebook a little over three years | |
ago, and I was working on the new product hear her. I | |
was working on the home page of the website. And I got | |
really familiar with writing mutations like this. I | |
would write some custom JavaScript and a custom server | |
end point. I would have the JavaScript call that end | |
point. I would have the end point return data and | |
basically whatever for mat I chose and enI would have | |
the JavaScript make sense of that data and up date | |
accordingly, usually by manually updating. | |
And then maybe I wanted to use that same owned | |
point from a different part of the client, I would need | |
to shove some logic in there to make sure that the | |
server was returning the data that the client needed, | |
regardless of where it was being called from. | |
And the key word here is custom. Basically | |
every time my teammates or I wanted to entered relevant | |
enter a new mutation, we had to start from scratch. I | |
personally really dislike this pattern of writing | |
mutations custom, it's all really representative. And | |
in my mind, this pattern becamesy no, ma'am, mouse, | |
because I had just started a Facebook, this is what I | |
did every time I would write JavaScript, so to me this | |
was JavaScript, so I came to myself as someone who | |
didn't like JavaScript and didn't to want write it. It | |
wasn't until I was introduced to flux that I don't just | |
like JavaScript, I just like this gross pattern. | |
So I would have have guessed three years ago | |
that I would now be on a team that I'm writing | |
JavaScript full-time or here with you guys at a | |
JavaScript conference but here I am and I'm excited | |
about it. | |
So back to Hughtation. We took a step in the | |
right direction when we introduced a more structured | |
API. So this was nice because it gave the client a | |
standardized way of what mutation they wanted to do and | |
also the input in the structured way. But what about | |
the data that the server runs? Each end point in the | |
graph is being used by a bunch of different clients, | |
there's not really a great way to make sure that the | |
server is going to return the data that the client needs | |
to update itself. | |
So what we would do instead is writing these | |
pretty minimal responses, just an ID to see if the | |
mutation was writing a comment, we would just get an ID | |
have the comment or maybe just saying this mutation is | |
[Indiscernible]. | |
Is and so at that point there's two main | |
appearances for how you update the client. You can | |
guess, you can say okay. I've got this, this is how it | |
works, so this is how I think it is. Or you can do a | |
second round trip, go back to the server and get the | |
data that you needed to make sure that it's right. | |
The first option has issues potentially, the | |
second has a few with deficiency. So either one of | |
those is ideal. | |
Ideally the response from the server would | |
contain exactly the information that the client needs to | |
operate itself. So let's talk about liking a story. | |
Let's say we have this mobile client, and it shows the | |
number of people who liked the story. Then we would. | |
The server to [Indiscernible] so that we could update | |
the story correctly. | |
And in our web client, instead of just showing | |
the number of likes, we show this thing thatta "we" call | |
a like sentence, and it's an international string that | |
we generate on the server about who else, who of your | |
friends has liked the story. So for this, we would want | |
the server to have new like components so that we can do | |
the upgrade. | |
Okay. But then what if we wanted to change | |
the mobile client to also show the profile pictures? | |
Then we would want to make sure to update the server so | |
that it also retains the new liked picture so that we | |
can show that when someone likes it. And then maybe if | |
we eventually took out those pictures, we would need to | |
cleanup that server end point and get rid of the serve | |
he, and in this equation insinuation it would start to | |
look familiar to you. In that situation where the | |
server end point needs to be aware of the details of the | |
client logic. | |
When we change the client rendering, when with | |
you can change when we return from the end point of the | |
server, and it gets time-consuming to keep those two | |
instant. | |
Just like before, relay can help us solve the | |
problem. It turns out that it doesn't just | |
[Indiscernible]. So how would this worktook? You just | |
want to write a query and get a response. Mutations are | |
a little bit different. So you need to provide three | |
pieces of information. The type of mutation that you're | |
trying to do, any input that will usually just be an | |
idea to, and then the query for the data that you wanted | |
after that mutation has been performed. | |
So for liking a story, what would this be? | |
The type is a story like. The inputs that you need to | |
give are just the ID of the story that you're liking, | |
and then the query can be anything, but in this case | |
let's say we want to know if the viewer liked the story | |
so that you can know if you can make that that thumb | |
blue and then the number of people that like it. | |
So when we send this information to SQL, it | |
performs a write, and then it runs that query, and then | |
it will send this back to payload, which we can use to | |
update the client. Relay uses graph mutations for all | |
of its data rights, which performs a standardized way to | |
perform the rights and then update the client afterward. | |
So let's look back at these three pieces of | |
information. One of the more interesting problems is | |
that we face when developing this new stream work for | |
relay was how to decide what this query. So so our goal | |
here was to get the client data consistent with the new | |
post mutation state of the world. So we want this query | |
to be for anything that we had in the store for the | |
relay that could have changed as a result of mutation. | |
So one option, the easiest option is just to | |
have the developer write these manually. So in this | |
example, they would write this query. But then let's | |
say somebody came and added profile pictures to it. You | |
would send this information to the server, the right | |
would happen, we would get this response back, and we | |
wouldn't have the new profile picture in the right size | |
and this was the bug. To avoid that, the person who | |
made the profile picture would need to make sure that | |
it's in the query and add the profile picture. And more | |
generally every time someone came some rendering lodge | |
being I, they would need to go find all the potentially | |
relevant mutation queries and change them. This wasn't | |
a great option for us because with relay you make a | |
change somewhere and then make a change in a whole budge | |
of other areas Pennsylvania. | |
So instead we put the mutation into relay | |
itself. | |
So it's a set of data that can change as a | |
result of that mutation. So this is an independent of | |
what any client rendered, it's a property of the | |
mutation itself. So of story life or comment. | |
So here's a set of things that can Chang when | |
we do a story life. And if we always queried for | |
everything that can change every time we move in a | |
mutation, we would clearly end up in a consistent state, | |
but we might be manifestly over fetching since we never | |
rendered any of that data that we don't care about. | |
So we have relay for of the set of data that | |
the client has retrieved for that and ID put into a | |
store. So if we had rendered my story, maybe this would | |
be the shot of the data that we had shot from my story. | |
And then when you do a mutation on some story or some | |
ID, he re: lay builds the mutation query by enter sect | |
this set of things that can possibly change with the set | |
of things that it cares about. | |
This incurs in the exact amount of field that | |
needs to be updated. | |
So in this case you would end up with this | |
query here. And the really nice part of this is that if | |
someone something come along and let's say they replace | |
that three likes with the like sentence. Then relay, | |
what we've stored will know that we've now fetched the | |
like sentence for that story and so when we do it in a | |
section, the enter secretary of state theory will be the | |
server for the new liked sentence rather than the liked | |
[Indiscernible] | |
So those are the basic of how relay mutations | |
work. The user takes the action, we send the name of | |
the mutation into relay, and relay does that | |
intersection to figure on what the query should be, it | |
will send that over to the server, the server will | |
respond, we'll put that response in the store, and then | |
know the reviews by sending them to the top. | |
And you'll notice that this diagram is similar | |
to the one that I showed you before. In particular the | |
second part is identical in the two flows. The SQL data | |
store and then the [Indiscernible]. | |
Between the secreted right flows just like | |
flux, relay is first class by using the same code to | |
both handle read and write. | |
So if you've used mutations before, when you | |
have the one working and the right on the server and the | |
client getting updated, there's often a lot more work to | |
be done. So maybe things, like, making the app feel | |
more responsive by having the client kind of doing | |
updates stainless. Or errors or resize. Because relay | |
has this have had framework, we're able to take a lot of | |
these common concerns and put them into relay themselves | |
so that they can get them for free rather than do them | |
again and again. | |
So let's start with this process example. The | |
way that I've described so far and then someone uses | |
this app and hit the like button, they noticed a | |
significant delay between when they hit like and when | |
the app actually changes because it's sitting there | |
waiting for the server to respond back. | |
It would be nice if we didn't have to deal | |
with this delay, and lucky for us relay provides the | |
supporter where we immediately under which date the view | |
to the effective post rate state being optimistic that | |
everything will work out nicely on the server. | |
Essentially to make this happen, you can | |
provide a payload mimicking the server response, and | |
then the view will change instantly based on that | |
payload. So that optimistic payload doesn't have to | |
include everything in that server update, it will | |
include everything you need to make feel right, so this | |
is we want it to feel good in between the server relay. | |
So maybe you can do something like this. That | |
would cause the thumb to turn blue, so maybe it's kind | |
of weird because [Indiscernible] instead of optimistic | |
where now you're also pulling the account by one and you | |
get the three likes and the blue thumb. | |
If I wanted to do an optimistic update like | |
this, I would to need add a bunch of code to manually | |
update. | |
or I could [Indiscernible] but in relay, I | |
just provide this optimistic field and then everything | |
else happened on that framework with automatically | |
updated. | |
So I want to describe now how these optimistic | |
mutations happen behind the scene. Even though the view | |
is changing immediately with optimistic update, relay | |
isn't immediately over writing the data. So instead we | |
maintain a Q of inflight mew takings, mutations that | |
we've done optimistic updates but the server response | |
doesn't come back. And when we read data from the | |
store, we read through that. So what does that look | |
like? Let's say this is the data in the store from that | |
stories. | |
So the UI is going to reflect behalf we have | |
in the store. And then let's say I do a like. So | |
you'll notice that the UI block immediately changed | |
blue, but the store, you haven't changed anything in the | |
store yet. | |
And now I do a comment. Again, because of the | |
optimistic update, the UI changes instantly, but the | |
store is still untouched. So now we give the server | |
payload, let's say the like succeeded. This is when | |
we'll actually remove the like from the queue and update | |
the story. | |
So now what if we get an error? So if we had | |
immediately written that comment into the store as soon | |
as I did it, this is when we'll be in a little bit of a | |
sticky situation because we need to roll back our | |
changes and makes that we left the store in the exact | |
same state as before. But instead of doing it with the | |
queue, all need to do is remove it from the queue and it | |
goes back. | |
The mutations queue also make it simple to | |
deal with retries. So let's say I'm trying to comment | |
and say thanks to my friends here. So I hit post and | |
the optimistic update happens immediately and then added | |
to the queue and then an error comes back. So in the | |
example I just showed you, it said there was an error, | |
take it out of the queue. But we can also keep it in | |
the queue but mark it with an error state. And then the | |
queue can pick up on this error state and show this | |
message, unable to post comment, try again. And then if | |
I do hit try again, it's easy to form that refry try to | |
if I have that sitting in the queue and it contains all | |
the data to send itself to the server again. | |
So I hit retry, and this time it worked and | |
voila. | |
So it provides a [Indiscernible] when someone | |
is performing a quick sequencing of mutations. So let's | |
say that I quickly like and unlike my story a bunch of | |
times in a row. There's a pretty high chance here that | |
something is going to go wrong if we just didn't have | |
any special mutation. | |
So there's a race condition for my mutations | |
in the server. If he ended up on a unlike, but the like | |
is the last one in the server, then we're going to have | |
the wrong data state on the server. And even if all | |
that works out, we have another payload for those coming | |
back. That if the last one coming back is a like, then | |
we're going to have a mutation state on the client. In | |
relay, we have a way to detect if these are independent | |
and guarantee that only one of them is in flight at a | |
time. | |
So the first one goes off and only when that | |
payload comes back do we send off the second one and so | |
on. So all the optimistic rates don't happen | |
immediately so the person not using the application | |
doesn't we're doing anything different behind the | |
scenes, but we do this to ensure. | |
So all of what I have described so far, relay | |
and mutation framework are already being used in | |
Facebook including or mobile app, both using relay and | |
react relay. I'm going to spend the last few minutes | |
here discussing a part of relay that we're still working | |
on that's not yet in production. | |
So this is a diagram that I showed earlier to | |
explain mutation. As I explained, replay lei can take | |
that mutation payload, store it, and then it send it. | |
So here the reaction is using the application. So that | |
doesn't have to be the case. So imagine if Joe comments | |
on my story from his phobe. You can actually use this | |
same pass once it goes through the cloud to send his | |
mutation to the relay, and then show his views. So we | |
call this a suband I want, basically, if I'm looking at | |
a new store, I can see all the comments on that store. | |
And it will provide a query saying here's the query for | |
what data I want for every new comment. | |
And then using a system in the back end, we | |
can ensure that all these mutations payloads are | |
delivered whenever someone comments and then relay will | |
automatically update. | |
So along with meteor or fire base, this | |
provides a simple way to build dynamic updates that feel | |
alive, and this is something we're excited about | |
innovating. | |
So I'm going to close with just a few last | |
points. Before the idea of relay, to just emphasize | |
that is that we should keep our data logic together with | |
our rendering logic because we found this approach to | |
really scale well to a big application being built by a | |
big team. | |
One of our main goals when we designed relay | |
was to identify these problematic Nantz people are | |
facing again and again and that are slowing them down | |
and to have the complexity of those patterns into relay. | |
So we saw a few of these examples today with relay, | |
thisth way we do that is the mutation query through the | |
way we have optimistic updates in the queue. | |
And in each case, someone using relay can have | |
this problem solve for free. So I'm going to close with | |
one last sticker comment. Thank you, all, for | |
listening. | |
[Clapping] | |
>> And if you guys have questions, I'll be | |
around the rest of the week. We'll be at our table out | |
there a lot of the time if you want to come talk to us, | |
we're excited to hear what you guys think. Thanks. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment